Consider defaultCandidate flag in case of no annotations as well

See gh-26528
This commit is contained in:
Juergen Hoeller 2024-02-20 15:30:38 +01:00
parent bc01e3116f
commit 63ca8d5d17
2 changed files with 62 additions and 39 deletions

View File

@ -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<? extends Annotation> 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<? extends Annotation> 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<? extends Annotation> 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<? extends Annotation> 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;
}
}
}
}

View File

@ -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<TestBean> plainBean) {
this.plainBean = plainBean.orElse(null);
}
}
@Bean(defaultCandidate=false) @Lazy @Qualifier("interesting")