Skip to content

Commit

Permalink
remove @Autowired / leave only tests to show it is possible via meta-…
Browse files Browse the repository at this point in the history
…annotation
  • Loading branch information
graemerocher committed May 17, 2024
1 parent 3815758 commit 69106dd
Show file tree
Hide file tree
Showing 15 changed files with 56 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import io.micronaut.context.Qualifier;
import io.micronaut.context.RequiresCondition;
import io.micronaut.context.annotation.Any;
import io.micronaut.context.annotation.Autowired;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.ConfigurationBuilder;
import io.micronaut.context.annotation.ConfigurationProperties;
Expand Down Expand Up @@ -1831,7 +1830,7 @@ private void visitFieldInjectionPointInternal(
boolean requiresReflection) {

boolean isRequired = fieldElement
.booleanValue(AnnotationUtil.INJECT, Autowired.MEMBER_REQUIRED)
.booleanValue(AnnotationUtil.INJECT, AnnotationUtil.MEMBER_REQUIRED)
.orElse(true);
boolean requiresGenericType = false;
Method methodToInvoke;
Expand Down Expand Up @@ -1954,7 +1953,7 @@ public void visitFieldValue(TypedElement declaringType,
visitFieldInjectionPointInternal(declaringType, fieldElement, annotationMetadata, requiresReflection);
} else if (!isConfigurationProperties || requiresReflection) {
boolean isRequired = fieldElement
.booleanValue(AnnotationUtil.INJECT, Autowired.MEMBER_REQUIRED)
.booleanValue(AnnotationUtil.INJECT, AnnotationUtil.MEMBER_REQUIRED)
.orElse(true);
visitFieldInjectionPointInternal(
declaringType,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
io.micronaut.inject.annotation.internal.AutowiredTransformer
io.micronaut.inject.annotation.internal.CoreNullableTransformer
io.micronaut.inject.annotation.internal.CoreNonNullTransformer
io.micronaut.inject.annotation.internal.KotlinNullableMapper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ public Annotation[] getDeclaredAnnotations() {
* The inherited annotation.
*/
public static final String ANN_INHERITED = Inherited.class.getName();
/**
* The name of the required member.
*/
public static final String MEMBER_REQUIRED = "required";

private static final Map<Integer, List<String>> INTERN_LIST_POOL = new ConcurrentHashMap<>();
private static final Map<String, Map<String, Object>> INTERN_MAP_POOL = new ConcurrentHashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.context.annotation;
package io.micronaut.inject.autowired;

import io.micronaut.context.annotation.AliasFor;
import io.micronaut.core.annotation.AnnotationUtil;
import jakarta.inject.Inject;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
Expand All @@ -36,10 +38,6 @@
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD})
@Documented
public @interface Autowired {
/**
* The name of the required member.
*/
String MEMBER_REQUIRED = "required";

/**
* Specify whether the injection point is required.
Expand All @@ -56,7 +54,7 @@
*/
@AliasFor(
annotation = Inject.class,
member = MEMBER_REQUIRED
member = AnnotationUtil.MEMBER_REQUIRED
)
boolean required() default true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class AutowiredSpec extends AbstractTypeElementSpec {
def context = buildContext('''
package test;
import io.micronaut.context.annotation.Autowired;
import io.micronaut.inject.autowired.Autowired;
import jakarta.inject.Singleton;
@Singleton
Expand All @@ -37,7 +37,7 @@ class Foo {
def context = buildContext('''
package test;
import io.micronaut.context.annotation.Autowired;
import io.micronaut.inject.autowired.Autowired;
import jakarta.inject.Singleton;
@Singleton
Expand All @@ -61,7 +61,7 @@ class Foo {
def context = buildContext('''
package test;
import io.micronaut.context.annotation.Autowired;
import io.micronaut.inject.autowired.Autowired;
import jakarta.inject.Singleton;
@Singleton
Expand All @@ -84,7 +84,7 @@ record Foo(String name) {
def context = buildContext('''
package test;
import io.micronaut.context.annotation.Autowired;
import io.micronaut.inject.autowired.Autowired;
import io.micronaut.context.annotation.Value;
import jakarta.inject.Singleton;
Expand All @@ -110,7 +110,7 @@ class Foo {
def context = buildContext('''
package test;
import io.micronaut.context.annotation.Autowired;
import io.micronaut.inject.autowired.Autowired;
import io.micronaut.context.annotation.Value;
import jakarta.inject.Singleton;
Expand Down Expand Up @@ -145,7 +145,7 @@ record Foo(@Value("injected") String name) {
def context = buildContext('''
package test;
import io.micronaut.context.annotation.Autowired;
import io.micronaut.inject.autowired.Autowired;
import io.micronaut.context.annotation.Value;
import jakarta.inject.Singleton;
Expand Down Expand Up @@ -179,7 +179,7 @@ record Foo(String name) {
def context = buildContext('''
package test;
import io.micronaut.context.annotation.Autowired;
import io.micronaut.inject.autowired.Autowired;
import io.micronaut.context.annotation.Value;
import jakarta.inject.Singleton;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.inject.annotation.internal;
package io.micronaut.inject.autowired;

import io.micronaut.context.annotation.Autowired;
import io.micronaut.core.annotation.AnnotationUtil;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.annotation.AnnotationValueBuilder;
Expand All @@ -32,8 +31,8 @@ public class AutowiredTransformer
@Override
public List<AnnotationValue<?>> transform(AnnotationValue<Autowired> annotation, VisitorContext visitorContext) {
AnnotationValueBuilder<Annotation> builder = AnnotationValue.builder(AnnotationUtil.INJECT);
annotation.booleanValue(Autowired.MEMBER_REQUIRED)
.ifPresent(b -> builder.member(Autowired.MEMBER_REQUIRED, b));
annotation.booleanValue(AnnotationUtil.MEMBER_REQUIRED)
.ifPresent(b -> builder.member(AnnotationUtil.MEMBER_REQUIRED, b));

return List.of(
builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ io.micronaut.aop.compile.AroundConstructCompileSpec$TestStereotypeAnnTransformer
io.micronaut.annotation.mapping.TransformsToRepeatableAnnotationSpec$TheAnnotationTransformer
io.micronaut.annotation.mapping.TransformNotInheritedAnnotationSpec$TheAnnotationMapper
io.micronaut.annotation.mapping.TransformToInheritedAnnotationSpec$TheAnnotationMapper
io.micronaut.inject.autowired.AutowiredTransformer
3 changes: 1 addition & 2 deletions inject/src/main/java/io/micronaut/inject/InjectionPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package io.micronaut.inject;

import io.micronaut.context.Qualifier;
import io.micronaut.context.annotation.Autowired;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationMetadataProvider;

Expand Down Expand Up @@ -55,7 +54,7 @@ default Qualifier<T> getDeclaringBeanQualifier() {
*/
static boolean isInjectionRequired(AnnotationMetadata annotationMetadata) {
return annotationMetadata != null && annotationMetadata
.booleanValue(AnnotationUtil.INJECT, Autowired.MEMBER_REQUIRED)
.booleanValue(AnnotationUtil.INJECT, AnnotationUtil.MEMBER_REQUIRED)
.orElse(true);
}

Expand Down
19 changes: 19 additions & 0 deletions src/main/docs/guide/ioc/injection/nullableInjection.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Occasionally it is desirable for injection to be optional (ie. not fail with a api:context.exceptions.NoSuchBeanException[] if there is no candidate bean available).

For example if you are trying to build an extensible system where a default implementation is provided by the type but consumers of your API can provide a bean that, if available, will be injected.

One way to make injection optional is to annotate the injected type with ann:core.annotation.Nullable[] which will result in `null` being injected by the framework if the bean is unavailable:

.Example of Injecting `null`
snippet::io.micronaut.docs.ioc.injection.nullable.Vehicle[indent="0"]

<1> Here the constructor argument is annotated with ann:core.annotation.Nullable[]
<2> Since there is no bean available `null` is injected and the code has to handle the possibility that the argument could be `null.

Using ann:core.annotation.Nullable[] has the following considerations:

* Can be used with any of the injection types (constructor, method or field injection)
* Somewhere the code has to handle what happens if `null` is injected, for constructors this is easy since the constructor can handle the `null`, but for fields and methods a `@PostConstruct` method would need to be implemented to handle `null` if `null` is not desirable.
* Finally, `@Nullable` cannot be used on primitive types like `int`, `long` etc. when using <<valueAnnotation, configuration injection>>. To handle primitives you need to specify `@Bindable(defaultValue="..")` and provide a default value.
19 changes: 1 addition & 18 deletions src/main/docs/guide/ioc/injection/optionalInjection.adoc
Original file line number Diff line number Diff line change
@@ -1,21 +1,4 @@
Occasionally it is desirable for injection to be optional (ie. not fail with a api:context.exceptions.NoSuchBeanException[] if there is no candidate bean available).

For example if you are trying to build an extensible system where a default implementation is provided by the type but consumers of your API can provide a bean that, if available, will be injected.

One way to make injection optional is to annotate the injected type with ann:core.annotation.Nullable[] which will result in `null` being injected by the framework if the bean is unavailable:

.Example of Injecting `null`
snippet::io.micronaut.docs.ioc.injection.nullable.Vehicle[indent="0"]

<1> Here the constructor argument is annotated with ann:core.annotation.Nullable[]
<2> Since there is no bean available `null` is injected and the code has to handle the possibility that the argument could be `null.

Using ann:core.annotation.Nullable[] has the following considerations:

* Can be used with any of the injection types (constructor, method or field injection)
* Somewhere the code has to handle what happens if `null` is injected, for constructors this is easy since the constructor can handle the `null`, but for fields and methods a `@PostConstruct` method would need to be implemented to handle `null` if `null` is not desirable.
* Finally, `@Nullable` cannot be used on primitive types like `int`, `long` etc. when using <<valueAnnotation, configuration injection>>. To handle primitives you need to specify `@Bindable(defaultValue="..")` and provide a default value.
// THIS SECTION IS DISABLED. See https://github.com/micronaut-projects/micronaut-core/pull/10830
An alternative to ann:core.annotation.Nullable[], is to make the injection optional (not to be confused with `java.util.Optional`). This can be done with the ann:context.annotation.Autowired[] annotation by setting the `required` member to `false`:

.Example of Optional Injection with `@Autowired`
Expand Down
2 changes: 1 addition & 1 deletion src/main/docs/guide/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ioc:
constructorInjection: Constructor Injection
fieldInjection: Field Injection
methodInjection: Method Injection
optionalInjection: Optional Injection
nullableInjection: Nullable Injection
types: Injectable Container Types
qualifiers: Bean Qualifiers
typed: Limiting Injectable Types
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package io.micronaut.docs.ioc.injection.optional

import io.micronaut.context.annotation.Autowired
// test disabled optional injection not supported
// see https://github.com/micronaut-projects/micronaut-core/pull/10830 for discussion
//import Autowired
import jakarta.inject.Singleton

@Singleton
class Vehicle {
@Autowired(required = false) // <1>
// @Autowired(required = false) // <1>
Engine engine = new Engine()

void start() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package io.micronaut.docs.ioc.injection.optional

import io.micronaut.context.annotation.Autowired
// test disabled optional injection not supported
// see https://github.com/micronaut-projects/micronaut-core/pull/10830 for discussion
//import io.micronaut.inject.autowired.Autowired
import jakarta.inject.Singleton

@Singleton
class Vehicle {
@Autowired(required = false)
// @io.micronaut.inject.autowired.Autowired(required = false)
var engine: Engine = Engine() // <1>

fun start() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

@MicronautTest(startApplication = false)
@Disabled("optional injection is not supported")
public class OptionalAutowiredTest {
@Test
void testVehicle(Vehicle vehicle) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package io.micronaut.docs.ioc.injection.optional;

import io.micronaut.context.annotation.Autowired;
//import io.micronaut.context.annotation.Autowired;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;

@Singleton
class Vehicle {
@Autowired(required = false) // <1>
//@Autowired(required = false) // <1>
@Inject
Engine engine = new Engine(6);

void start() {
Expand Down

0 comments on commit 69106dd

Please sign in to comment.