From 3b506e11a1ab1a39c309604f0b0c38fd0e8d3952 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:24:57 +0300 Subject: [PATCH 1/3] Polishing --- ...erAnnotationAutowireCandidateResolver.java | 8 +- .../InjectAnnotationAutowireContextTests.java | 292 +++++++++--------- 2 files changed, 142 insertions(+), 158 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java index 0c238f4d569..8cf27dfc839 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java @@ -82,8 +82,8 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa } /** - * Create a new QualifierAnnotationAutowireCandidateResolver - * for the given qualifier annotation type. + * Create a new {@code QualifierAnnotationAutowireCandidateResolver} for the given + * qualifier annotation type. * @param qualifierType the qualifier annotation to look for */ public QualifierAnnotationAutowireCandidateResolver(Class qualifierType) { @@ -92,8 +92,8 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa } /** - * Create a new QualifierAnnotationAutowireCandidateResolver - * for the given qualifier annotation types. + * Create a new {@code QualifierAnnotationAutowireCandidateResolver} for the given + * qualifier annotation types. * @param qualifierTypes the qualifier annotations to look for */ public QualifierAnnotationAutowireCandidateResolver(Set> qualifierTypes) { diff --git a/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java b/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java index 54dc5370f2d..b4353ea40ae 100644 --- a/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java +++ b/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java @@ -46,70 +46,71 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; */ class InjectAnnotationAutowireContextTests { + private static final String PERSON1 = "person1"; + + private static final String PERSON2 = "person2"; + private static final String JUERGEN = "juergen"; private static final String MARK = "mark"; @Test - void testAutowiredFieldWithSingleNonQualifiedCandidate() { + void autowiredFieldWithSingleNonQualifiedCandidate() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs = new ConstructorArgumentValues(); cavs.addGenericArgumentValue(JUERGEN); RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null); - context.registerBeanDefinition(JUERGEN, person); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedFieldTestBean.class)); + context.registerBeanDefinition(PERSON1, person); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy( - context::refresh) - .satisfies(ex -> { - assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); - assertThat(ex.getBeanName()).isEqualTo("autowired"); - }); + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(context::refresh) + .satisfies(ex -> { + assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThat(ex.getBeanName()).isEqualTo("autowired"); + }); } @Test - void testAutowiredMethodParameterWithSingleNonQualifiedCandidate() { + void autowiredMethodParameterWithSingleNonQualifiedCandidate() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs = new ConstructorArgumentValues(); cavs.addGenericArgumentValue(JUERGEN); RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null); - context.registerBeanDefinition(JUERGEN, person); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); + context.registerBeanDefinition(PERSON1, person); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy( - context::refresh) - .satisfies(ex -> { - assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); - assertThat(ex.getBeanName()).isEqualTo("autowired"); - }); + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(context::refresh) + .satisfies(ex -> { + assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThat(ex.getBeanName()).isEqualTo("autowired"); + }); } @Test - void testAutowiredConstructorArgumentWithSingleNonQualifiedCandidate() { + void autowiredConstructorArgumentWithSingleNonQualifiedCandidate() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs = new ConstructorArgumentValues(); cavs.addGenericArgumentValue(JUERGEN); RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null); - context.registerBeanDefinition(JUERGEN, person); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class)); + context.registerBeanDefinition(PERSON1, person); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); - assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy( - context::refresh) - .satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired")); + assertThatExceptionOfType(UnsatisfiedDependencyException.class) + .isThrownBy(context::refresh) + .satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired")); } @Test - void testAutowiredFieldWithSingleQualifiedCandidate() { + void autowiredFieldWithSingleQualifiedCandidate() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs = new ConstructorArgumentValues(); cavs.addGenericArgumentValue(JUERGEN); RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null); person.addQualifier(new AutowireCandidateQualifier(TestQualifier.class)); - context.registerBeanDefinition(JUERGEN, person); + context.registerBeanDefinition(PERSON1, person); context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); @@ -118,15 +119,14 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredMethodParameterWithSingleQualifiedCandidate() { + void autowiredMethodParameterWithSingleQualifiedCandidate() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs = new ConstructorArgumentValues(); cavs.addGenericArgumentValue(JUERGEN); RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null); person.addQualifier(new AutowireCandidateQualifier(TestQualifier.class)); - context.registerBeanDefinition(JUERGEN, person); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); + context.registerBeanDefinition(PERSON1, person); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); QualifiedMethodParameterTestBean bean = @@ -135,15 +135,14 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredMethodParameterWithStaticallyQualifiedCandidate() { + void autowiredMethodParameterWithStaticallyQualifiedCandidate() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs = new ConstructorArgumentValues(); cavs.addGenericArgumentValue(JUERGEN); RootBeanDefinition person = new RootBeanDefinition(QualifiedPerson.class, cavs, null); - context.registerBeanDefinition(JUERGEN, + context.registerBeanDefinition(PERSON1, ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(person, JUERGEN), context, true).getBeanDefinition()); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); QualifiedMethodParameterTestBean bean = @@ -152,18 +151,17 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredMethodParameterWithStaticallyQualifiedCandidateAmongOthers() { + void autowiredMethodParameterWithStaticallyQualifiedCandidateAmongOthers() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs = new ConstructorArgumentValues(); cavs.addGenericArgumentValue(JUERGEN); - RootBeanDefinition person = new RootBeanDefinition(QualifiedPerson.class, cavs, null); + RootBeanDefinition person1 = new RootBeanDefinition(QualifiedPerson.class, cavs, null); ConstructorArgumentValues cavs2 = new ConstructorArgumentValues(); cavs2.addGenericArgumentValue(MARK); RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null); - context.registerBeanDefinition(JUERGEN, person); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); QualifiedMethodParameterTestBean bean = @@ -172,15 +170,14 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredConstructorArgumentWithSingleQualifiedCandidate() { + void autowiredConstructorArgumentWithSingleQualifiedCandidate() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs = new ConstructorArgumentValues(); cavs.addGenericArgumentValue(JUERGEN); RootBeanDefinition person = new RootBeanDefinition(Person.class, cavs, null); person.addQualifier(new AutowireCandidateQualifier(TestQualifier.class)); - context.registerBeanDefinition(JUERGEN, person); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class)); + context.registerBeanDefinition(PERSON1, person); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); QualifiedConstructorArgumentTestBean bean = @@ -189,7 +186,7 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredFieldWithMultipleNonQualifiedCandidates() { + void autowiredFieldWithMultipleNonQualifiedCandidates() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -197,21 +194,20 @@ class InjectAnnotationAutowireContextTests { ConstructorArgumentValues cavs2 = new ConstructorArgumentValues(); cavs2.addGenericArgumentValue(MARK); RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedFieldTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy( - context::refresh) - .satisfies(ex -> { - assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); - assertThat(ex.getBeanName()).isEqualTo("autowired"); - }); + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(context::refresh) + .satisfies(ex -> { + assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThat(ex.getBeanName()).isEqualTo("autowired"); + }); } @Test - void testAutowiredMethodParameterWithMultipleNonQualifiedCandidates() { + void autowiredMethodParameterWithMultipleNonQualifiedCandidates() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -219,21 +215,20 @@ class InjectAnnotationAutowireContextTests { ConstructorArgumentValues cavs2 = new ConstructorArgumentValues(); cavs2.addGenericArgumentValue(MARK); RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy( - context::refresh) - .satisfies(ex -> { - assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); - assertThat(ex.getBeanName()).isEqualTo("autowired"); - }); + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(context::refresh) + .satisfies(ex -> { + assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThat(ex.getBeanName()).isEqualTo("autowired"); + }); } @Test - void testAutowiredConstructorArgumentWithMultipleNonQualifiedCandidates() { + void autowiredConstructorArgumentWithMultipleNonQualifiedCandidates() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -241,18 +236,17 @@ class InjectAnnotationAutowireContextTests { ConstructorArgumentValues cavs2 = new ConstructorArgumentValues(); cavs2.addGenericArgumentValue(MARK); RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); - assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy( - context::refresh) - .satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired")); + assertThatExceptionOfType(UnsatisfiedDependencyException.class) + .isThrownBy(context::refresh) + .satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired")); } @Test - void testAutowiredFieldResolvesQualifiedCandidate() { + void autowiredFieldResolvesQualifiedCandidate() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -261,10 +255,9 @@ class InjectAnnotationAutowireContextTests { ConstructorArgumentValues cavs2 = new ConstructorArgumentValues(); cavs2.addGenericArgumentValue(MARK); RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedFieldTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); QualifiedFieldTestBean bean = (QualifiedFieldTestBean) context.getBean("autowired"); @@ -272,7 +265,7 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredMethodParameterResolvesQualifiedCandidate() { + void autowiredMethodParameterResolvesQualifiedCandidate() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -281,10 +274,9 @@ class InjectAnnotationAutowireContextTests { ConstructorArgumentValues cavs2 = new ConstructorArgumentValues(); cavs2.addGenericArgumentValue(MARK); RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedMethodParameterTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); QualifiedMethodParameterTestBean bean = @@ -293,7 +285,7 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredConstructorArgumentResolvesQualifiedCandidate() { + void autowiredConstructorArgumentResolvesQualifiedCandidate() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -302,10 +294,9 @@ class InjectAnnotationAutowireContextTests { ConstructorArgumentValues cavs2 = new ConstructorArgumentValues(); cavs2.addGenericArgumentValue(MARK); RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); QualifiedConstructorArgumentTestBean bean = @@ -314,7 +305,7 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredFieldResolvesQualifiedCandidateWithDefaultValueAndNoValueOnBeanDefinition() { + void autowiredFieldResolvesQualifiedCandidateWithDefaultValueAndNoValueOnBeanDefinition() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -324,10 +315,9 @@ class InjectAnnotationAutowireContextTests { ConstructorArgumentValues cavs2 = new ConstructorArgumentValues(); cavs2.addGenericArgumentValue(MARK); RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); QualifiedFieldWithDefaultValueTestBean bean = @@ -336,7 +326,7 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredFieldDoesNotResolveCandidateWithDefaultValueAndConflictingValueOnBeanDefinition() { + void autowiredFieldDoesNotResolveCandidateWithDefaultValueAndConflictingValueOnBeanDefinition() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -346,21 +336,20 @@ class InjectAnnotationAutowireContextTests { ConstructorArgumentValues cavs2 = new ConstructorArgumentValues(); cavs2.addGenericArgumentValue(MARK); RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy( - context::refresh) - .satisfies(ex -> { - assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); - assertThat(ex.getBeanName()).isEqualTo("autowired"); - }); + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(context::refresh) + .satisfies(ex -> { + assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThat(ex.getBeanName()).isEqualTo("autowired"); + }); } @Test - void testAutowiredFieldResolvesWithDefaultValueAndExplicitDefaultValueOnBeanDefinition() { + void autowiredFieldResolvesWithDefaultValueAndExplicitDefaultValueOnBeanDefinition() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -370,10 +359,9 @@ class InjectAnnotationAutowireContextTests { ConstructorArgumentValues cavs2 = new ConstructorArgumentValues(); cavs2.addGenericArgumentValue(MARK); RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); QualifiedFieldWithDefaultValueTestBean bean = @@ -382,7 +370,7 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredFieldResolvesWithMultipleQualifierValues() { + void autowiredFieldResolvesWithMultipleQualifierValues() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -396,10 +384,9 @@ class InjectAnnotationAutowireContextTests { AutowireCandidateQualifier qualifier2 = new AutowireCandidateQualifier(TestQualifierWithMultipleAttributes.class); qualifier2.setAttribute("number", 123); person2.addQualifier(qualifier2); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); QualifiedFieldWithMultipleAttributesTestBean bean = @@ -408,7 +395,7 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredFieldDoesNotResolveWithMultipleQualifierValuesAndConflictingDefaultValue() { + void autowiredFieldDoesNotResolveWithMultipleQualifierValuesAndConflictingDefaultValue() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -423,21 +410,20 @@ class InjectAnnotationAutowireContextTests { qualifier2.setAttribute("number", 123); qualifier2.setAttribute("value", "not the default"); person2.addQualifier(qualifier2); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy( - context::refresh) - .satisfies(ex -> { - assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); - assertThat(ex.getBeanName()).isEqualTo("autowired"); - }); + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(context::refresh) + .satisfies(ex -> { + assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThat(ex.getBeanName()).isEqualTo("autowired"); + }); } @Test - void testAutowiredFieldResolvesWithMultipleQualifierValuesAndExplicitDefaultValue() { + void autowiredFieldResolvesWithMultipleQualifierValuesAndExplicitDefaultValue() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -452,10 +438,9 @@ class InjectAnnotationAutowireContextTests { qualifier2.setAttribute("number", 123); qualifier2.setAttribute("value", "default"); person2.addQualifier(qualifier2); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); context.refresh(); QualifiedFieldWithMultipleAttributesTestBean bean = @@ -464,7 +449,7 @@ class InjectAnnotationAutowireContextTests { } @Test - void testAutowiredFieldDoesNotResolveWithMultipleQualifierValuesAndMultipleMatchingCandidates() { + void autowiredFieldDoesNotResolveWithMultipleQualifierValuesAndMultipleMatchingCandidates() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue(JUERGEN); @@ -479,38 +464,37 @@ class InjectAnnotationAutowireContextTests { qualifier2.setAttribute("number", 123); qualifier2.setAttribute("value", "default"); person2.addQualifier(qualifier2); - context.registerBeanDefinition(JUERGEN, person1); - context.registerBeanDefinition(MARK, person2); - context.registerBeanDefinition("autowired", - new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class)); + context.registerBeanDefinition(PERSON1, person1); + context.registerBeanDefinition(PERSON2, person2); + context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy( - context::refresh) - .satisfies(ex -> { - assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); - assertThat(ex.getBeanName()).isEqualTo("autowired"); - }); + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(context::refresh) + .satisfies(ex -> { + assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class); + assertThat(ex.getBeanName()).isEqualTo("autowired"); + }); } @Test - void testAutowiredFieldDoesNotResolveWithBaseQualifierAndNonDefaultValueAndMultipleMatchingCandidates() { + void autowiredConstructorArgumentDoesNotResolveWithBaseQualifierAndNonDefaultValueAndMultipleMatchingCandidates() { GenericApplicationContext context = new GenericApplicationContext(); ConstructorArgumentValues cavs1 = new ConstructorArgumentValues(); cavs1.addGenericArgumentValue("the real juergen"); RootBeanDefinition person1 = new RootBeanDefinition(Person.class, cavs1, null); - person1.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "juergen")); + person1.addQualifier(new AutowireCandidateQualifier(Qualifier.class, JUERGEN)); ConstructorArgumentValues cavs2 = new ConstructorArgumentValues(); cavs2.addGenericArgumentValue("juergen imposter"); RootBeanDefinition person2 = new RootBeanDefinition(Person.class, cavs2, null); - person2.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "juergen")); + person2.addQualifier(new AutowireCandidateQualifier(Qualifier.class, JUERGEN)); context.registerBeanDefinition("juergen1", person1); context.registerBeanDefinition("juergen2", person2); context.registerBeanDefinition("autowired", new RootBeanDefinition(QualifiedConstructorArgumentWithBaseQualifierNonDefaultValueTestBean.class)); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); - assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy( - context::refresh) - .satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired")); + assertThatExceptionOfType(UnsatisfiedDependencyException.class) + .isThrownBy(context::refresh) + .satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired")); } @@ -543,7 +527,7 @@ class InjectAnnotationAutowireContextTests { private static class QualifiedConstructorArgumentTestBean { - private Person person; + private final Person person; @Inject public QualifiedConstructorArgumentTestBean(@TestQualifier Person person) { @@ -593,13 +577,13 @@ class InjectAnnotationAutowireContextTests { } - public static class QualifiedConstructorArgumentWithBaseQualifierNonDefaultValueTestBean { + static class QualifiedConstructorArgumentWithBaseQualifierNonDefaultValueTestBean { private Person person; @Inject public QualifiedConstructorArgumentWithBaseQualifierNonDefaultValueTestBean( - @Named("juergen") Person person) { + @Named(JUERGEN) Person person) { this.person = person; } From f9d2641fd59a39b149277c707dd277922c3aa5d7 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Thu, 8 Aug 2024 13:11:48 +0300 Subject: [PATCH 2/3] Update field name to reflect switch to Jakarta --- .../factory/support/DefaultListableBeanFactory.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index ae601926028..d119caefd3b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -123,16 +123,16 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { @Nullable - private static Class javaxInjectProviderClass; + private static Class jakartaInjectProviderClass; static { try { - javaxInjectProviderClass = + jakartaInjectProviderClass = ClassUtils.forName("jakarta.inject.Provider", DefaultListableBeanFactory.class.getClassLoader()); } catch (ClassNotFoundException ex) { // JSR-330 API not available - Provider interface simply not supported then. - javaxInjectProviderClass = null; + jakartaInjectProviderClass = null; } } @@ -1340,7 +1340,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto ObjectProvider.class == descriptor.getDependencyType()) { return new DependencyObjectProvider(descriptor, requestingBeanName); } - else if (javaxInjectProviderClass == descriptor.getDependencyType()) { + else if (jakartaInjectProviderClass == descriptor.getDependencyType()) { return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName); } else if (descriptor.supportsLazyResolution()) { From bcffa15c7dd3faeb18391f096961620e225a52db Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:25:31 +0300 Subject: [PATCH 3/3] =?UTF-8?q?Reinstate=20qualifier=20support=20for=20JSR?= =?UTF-8?q?-330=20@=E2=81=A0javax.inject.Named?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit revises QualifierAnnotationAutowireCandidateResolver to reinstate "qualifier" support for the legacy JSR-330 @⁠javax.inject.Named annotation. See gh-31090 Closes gh-33345 --- spring-beans/spring-beans.gradle | 1 + .../JakartaAnnotationsRuntimeHints.java | 16 ++- ...erAnnotationAutowireCandidateResolver.java | 18 ++- .../JakartaAnnotationsRuntimeHintsTests.java | 13 +- .../InjectAnnotationAutowireContextTests.java | 117 +++++++++++++++++- 5 files changed, 155 insertions(+), 10 deletions(-) diff --git a/spring-beans/spring-beans.gradle b/spring-beans/spring-beans.gradle index b407bf0ed24..c4fb10eb320 100644 --- a/spring-beans/spring-beans.gradle +++ b/spring-beans/spring-beans.gradle @@ -16,4 +16,5 @@ dependencies { testImplementation(project(":spring-core-test")) testImplementation(testFixtures(project(":spring-core"))) testImplementation("jakarta.annotation:jakarta.annotation-api") + testImplementation("javax.inject:javax.inject") } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java index c4e46f8ecd8..439b1fb30e4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,16 +24,24 @@ import org.springframework.aot.hint.TypeReference; import org.springframework.lang.Nullable; /** - * {@link RuntimeHintsRegistrar} for Jakarta annotations. + * {@link RuntimeHintsRegistrar} for Jakarta annotations and their pre-Jakarta equivalents. * * @author Brian Clozel + * @author Sam Brannen */ class JakartaAnnotationsRuntimeHints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { - Stream.of("jakarta.inject.Inject", "jakarta.inject.Provider", "jakarta.inject.Qualifier").forEach(typeName -> - hints.reflection().registerType(TypeReference.of(typeName))); + // javax.inject.Provider is omitted from the list, since we do not currently load + // it via reflection. + Stream.of( + "jakarta.inject.Inject", + "jakarta.inject.Provider", + "jakarta.inject.Qualifier", + "javax.inject.Inject", + "javax.inject.Qualifier" + ).forEach(typeName -> hints.reflection().registerType(TypeReference.of(typeName))); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java index 8cf27dfc839..cf552f996e0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java @@ -47,11 +47,13 @@ import org.springframework.util.ObjectUtils; * against {@link Qualifier qualifier annotations} on the field or parameter to be autowired. * Also supports suggested expression values through a {@link Value value} annotation. * - *

Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation, if available. + *

Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation (as well as its + * pre-Jakarta {@code javax.inject.Qualifier} equivalent), if available. * * @author Mark Fisher * @author Juergen Hoeller * @author Stephane Nicoll + * @author Sam Brannen * @since 2.5 * @see AutowireCandidateQualifier * @see Qualifier @@ -65,9 +67,10 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa /** - * Create a new QualifierAnnotationAutowireCandidateResolver - * for Spring's standard {@link Qualifier} annotation. - *

Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation, if available. + * Create a new {@code QualifierAnnotationAutowireCandidateResolver} for Spring's + * standard {@link Qualifier} annotation. + *

Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation (as well as + * its pre-Jakarta {@code javax.inject.Qualifier} equivalent), if available. */ @SuppressWarnings("unchecked") public QualifierAnnotationAutowireCandidateResolver() { @@ -76,6 +79,13 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa this.qualifierTypes.add((Class) ClassUtils.forName("jakarta.inject.Qualifier", QualifierAnnotationAutowireCandidateResolver.class.getClassLoader())); } + catch (ClassNotFoundException ex) { + // JSR-330 API (as included in Jakarta EE) not available - simply skip. + } + try { + this.qualifierTypes.add((Class) ClassUtils.forName("javax.inject.Qualifier", + QualifierAnnotationAutowireCandidateResolver.class.getClassLoader())); + } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java index 9dae6cf67cb..ef2e236fb68 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/JakartaAnnotationsRuntimeHintsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link JakartaAnnotationsRuntimeHints}. * * @author Brian Clozel + * @author Sam Brannen */ class JakartaAnnotationsRuntimeHintsTests { @@ -62,4 +63,14 @@ class JakartaAnnotationsRuntimeHintsTests { assertThat(RuntimeHintsPredicates.reflection().onType(Qualifier.class)).accepts(this.hints); } + @Test // gh-33345 + void javaxInjectAnnotationHasHints() { + assertThat(RuntimeHintsPredicates.reflection().onType(javax.inject.Inject.class)).accepts(this.hints); + } + + @Test // gh-33345 + void javaxQualifierAnnotationHasHints() { + assertThat(RuntimeHintsPredicates.reflection().onType(javax.inject.Qualifier.class)).accepts(this.hints); + } + } diff --git a/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java b/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java index b4353ea40ae..48d19fbf7e8 100644 --- a/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java +++ b/spring-context/src/test/java/org/springframework/beans/factory/support/InjectAnnotationAutowireContextTests.java @@ -32,6 +32,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.support.GenericApplicationContext; @@ -39,9 +40,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** - * Integration tests for handling JSR-303 {@link jakarta.inject.Qualifier} annotations. + * Integration tests for handling JSR-330 {@link jakarta.inject.Qualifier} and + * {@link javax.inject.Qualifier} annotations. * * @author Juergen Hoeller + * @author Sam Brannen * @since 3.0 */ class InjectAnnotationAutowireContextTests { @@ -304,6 +307,26 @@ class InjectAnnotationAutowireContextTests { assertThat(bean.getPerson().getName()).isEqualTo(JUERGEN); } + @Test // gh-33345 + void autowiredConstructorArgumentResolvesJakartaNamedCandidate() { + Class testBeanClass = JakartaNamedConstructorArgumentTestBean.class; + AnnotationConfigApplicationContext context = + new AnnotationConfigApplicationContext(testBeanClass, JakartaCat.class, JakartaDog.class); + JakartaNamedConstructorArgumentTestBean bean = context.getBean(testBeanClass); + assertThat(bean.getAnimal1().getName()).isEqualTo("Jakarta Tiger"); + assertThat(bean.getAnimal2().getName()).isEqualTo("Jakarta Fido"); + } + + @Test // gh-33345 + void autowiredConstructorArgumentResolvesJavaxNamedCandidate() { + Class testBeanClass = JavaxNamedConstructorArgumentTestBean.class; + AnnotationConfigApplicationContext context = + new AnnotationConfigApplicationContext(testBeanClass, JavaxCat.class, JavaxDog.class); + JavaxNamedConstructorArgumentTestBean bean = context.getBean(testBeanClass); + assertThat(bean.getAnimal1().getName()).isEqualTo("Javax Tiger"); + assertThat(bean.getAnimal2().getName()).isEqualTo("Javax Fido"); + } + @Test void autowiredFieldResolvesQualifiedCandidateWithDefaultValueAndNoValueOnBeanDefinition() { GenericApplicationContext context = new GenericApplicationContext(); @@ -541,6 +564,52 @@ class InjectAnnotationAutowireContextTests { } + static class JakartaNamedConstructorArgumentTestBean { + + private final Animal animal1; + private final Animal animal2; + + @jakarta.inject.Inject + public JakartaNamedConstructorArgumentTestBean(@jakarta.inject.Named("Cat") Animal animal1, + @jakarta.inject.Named("Dog") Animal animal2) { + + this.animal1 = animal1; + this.animal2 = animal2; + } + + public Animal getAnimal1() { + return this.animal1; + } + + public Animal getAnimal2() { + return this.animal2; + } + } + + + static class JavaxNamedConstructorArgumentTestBean { + + private final Animal animal1; + private final Animal animal2; + + @javax.inject.Inject + public JavaxNamedConstructorArgumentTestBean(@javax.inject.Named("Cat") Animal animal1, + @javax.inject.Named("Dog") Animal animal2) { + + this.animal1 = animal1; + this.animal2 = animal2; + } + + public Animal getAnimal1() { + return this.animal1; + } + + public Animal getAnimal2() { + return this.animal2; + } + } + + public static class QualifiedFieldWithDefaultValueTestBean { @Inject @@ -620,6 +689,52 @@ class InjectAnnotationAutowireContextTests { } + interface Animal { + + String getName(); + } + + + @jakarta.inject.Named("Cat") + static class JakartaCat implements Animal { + + @Override + public String getName() { + return "Jakarta Tiger"; + } + } + + + @javax.inject.Named("Cat") + static class JavaxCat implements Animal { + + @Override + public String getName() { + return "Javax Tiger"; + } + } + + + @jakarta.inject.Named("Dog") + static class JakartaDog implements Animal { + + @Override + public String getName() { + return "Jakarta Fido"; + } + } + + + @javax.inject.Named("Dog") + static class JavaxDog implements Animal { + + @Override + public String getName() { + return "Javax Fido"; + } + } + + @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier