From 63ca8d5d17fa6f70e3dd71ca3009f4593cd6f7f4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 20 Feb 2024 15:30:38 +0100 Subject: [PATCH] Consider defaultCandidate flag in case of no annotations as well See gh-26528 --- ...erAnnotationAutowireCandidateResolver.java | 63 +++++++++---------- .../BeanMethodQualificationTests.java | 38 ++++++++--- 2 files changed, 62 insertions(+), 39 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 4b6605984e5..0c2e6d609dc 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 @@ -164,41 +164,40 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa * Match the given qualifier annotations against the candidate bean definition. */ protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) { - if (ObjectUtils.isEmpty(annotationsToSearch)) { - return true; - } boolean qualifierFound = false; - SimpleTypeConverter typeConverter = new SimpleTypeConverter(); - for (Annotation annotation : annotationsToSearch) { - Class type = annotation.annotationType(); - boolean checkMeta = true; - boolean fallbackToMeta = false; - if (isQualifier(type)) { - qualifierFound = true; - if (!checkQualifier(bdHolder, annotation, typeConverter)) { - fallbackToMeta = true; - } - else { - checkMeta = false; - } - } - if (checkMeta) { - boolean foundMeta = false; - for (Annotation metaAnn : type.getAnnotations()) { - Class metaType = metaAnn.annotationType(); - if (isQualifier(metaType)) { - qualifierFound = true; - foundMeta = true; - // Only accept fallback match if @Qualifier annotation has a value... - // Otherwise, it is just a marker for a custom qualifier annotation. - if ((fallbackToMeta && ObjectUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) || - !checkQualifier(bdHolder, metaAnn, typeConverter)) { - return false; - } + if (!ObjectUtils.isEmpty(annotationsToSearch)) { + SimpleTypeConverter typeConverter = new SimpleTypeConverter(); + for (Annotation annotation : annotationsToSearch) { + Class type = annotation.annotationType(); + boolean checkMeta = true; + boolean fallbackToMeta = false; + if (isQualifier(type)) { + qualifierFound = true; + if (!checkQualifier(bdHolder, annotation, typeConverter)) { + fallbackToMeta = true; + } + else { + checkMeta = false; } } - if (fallbackToMeta && !foundMeta) { - return false; + if (checkMeta) { + boolean foundMeta = false; + for (Annotation metaAnn : type.getAnnotations()) { + Class metaType = metaAnn.annotationType(); + if (isQualifier(metaType)) { + qualifierFound = true; + foundMeta = true; + // Only accept fallback match if @Qualifier annotation has a value... + // Otherwise, it is just a marker for a custom qualifier annotation. + if ((fallbackToMeta && ObjectUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) || + !checkQualifier(bdHolder, metaAnn, typeConverter)) { + return false; + } + } + } + if (fallbackToMeta && !foundMeta) { + return false; + } } } } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/BeanMethodQualificationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/BeanMethodQualificationTests.java index cd969c928d5..d9ed7e70d03 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/BeanMethodQualificationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/BeanMethodQualificationTests.java @@ -18,6 +18,7 @@ package org.springframework.context.annotation.configuration; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Optional; import org.junit.jupiter.api.Test; @@ -85,20 +86,26 @@ class BeanMethodQualificationTests { @Test void primary() { AnnotationConfigApplicationContext ctx = - new AnnotationConfigApplicationContext(PrimaryConfig.class, StandardPojo.class); + new AnnotationConfigApplicationContext(PrimaryConfig.class, StandardPojo.class, ConstructorPojo.class); StandardPojo pojo = ctx.getBean(StandardPojo.class); assertThat(pojo.testBean.getName()).isEqualTo("interesting"); assertThat(pojo.testBean2.getName()).isEqualTo("boring"); + ConstructorPojo pojo2 = ctx.getBean(ConstructorPojo.class); + assertThat(pojo2.testBean.getName()).isEqualTo("interesting"); + assertThat(pojo2.testBean2.getName()).isEqualTo("boring"); ctx.close(); } @Test void fallback() { AnnotationConfigApplicationContext ctx = - new AnnotationConfigApplicationContext(FallbackConfig.class, StandardPojo.class); + new AnnotationConfigApplicationContext(FallbackConfig.class, StandardPojo.class, ConstructorPojo.class); StandardPojo pojo = ctx.getBean(StandardPojo.class); assertThat(pojo.testBean.getName()).isEqualTo("interesting"); assertThat(pojo.testBean2.getName()).isEqualTo("boring"); + ConstructorPojo pojo2 = ctx.getBean(ConstructorPojo.class); + assertThat(pojo2.testBean.getName()).isEqualTo("interesting"); + assertThat(pojo2.testBean2.getName()).isEqualTo("boring"); ctx.close(); } @@ -233,7 +240,7 @@ class BeanMethodQualificationTests { @Bean @Qualifier("interesting") public static TestBean testBean1x() { - return new TestBean("interesting"); + return new TestBean(""); } @Bean @Boring @Primary @@ -245,7 +252,7 @@ class BeanMethodQualificationTests { @Bean @Boring public TestBean testBean2x() { - return new TestBean("boring"); + return new TestBean(""); } } @@ -259,7 +266,7 @@ class BeanMethodQualificationTests { @Bean @Qualifier("interesting") @Fallback public static TestBean testBean1x() { - return new TestBean("interesting"); + return new TestBean(""); } @Bean @Boring @@ -271,7 +278,7 @@ class BeanMethodQualificationTests { @Bean @Boring @Fallback public TestBean testBean2x() { - return new TestBean("boring"); + return new TestBean(""); } } @@ -283,6 +290,19 @@ class BeanMethodQualificationTests { @Autowired @Boring TestBean testBean2; } + @Component @Lazy + static class ConstructorPojo { + + TestBean testBean; + + TestBean testBean2; + + ConstructorPojo(@Qualifier("interesting") TestBean testBean, @Boring TestBean testBean2) { + this.testBean = testBean; + this.testBean2 = testBean2; + } + } + @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Boring { @@ -323,11 +343,15 @@ class BeanMethodQualificationTests { @InterestingPojo static class CustomPojo { - @Autowired(required=false) TestBean plainBean; + TestBean plainBean; @InterestingNeed TestBean testBean; @InterestingNeedWithRequiredOverride(required=false) NestedTestBean nestedTestBean; + + public CustomPojo(Optional plainBean) { + this.plainBean = plainBean.orElse(null); + } } @Bean(defaultCandidate=false) @Lazy @Qualifier("interesting")