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 2c37dd7a55..4281e65394 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 @@ -154,26 +154,35 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa */ @Override public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) { - boolean match = super.isAutowireCandidate(bdHolder, descriptor); - if (match) { - match = checkQualifiers(bdHolder, descriptor.getAnnotations()); - if (match) { - MethodParameter methodParam = descriptor.getMethodParameter(); - if (methodParam != null) { - Method method = methodParam.getMethod(); - if (method == null || void.class == method.getReturnType()) { - match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations()); + if (!super.isAutowireCandidate(bdHolder, descriptor)) { + return false; + } + Boolean checked = checkQualifiers(bdHolder, descriptor.getAnnotations()); + if (checked != Boolean.FALSE) { + MethodParameter methodParam = descriptor.getMethodParameter(); + if (methodParam != null) { + Method method = methodParam.getMethod(); + if (method == null || void.class == method.getReturnType()) { + Boolean methodChecked = checkQualifiers(bdHolder, methodParam.getMethodAnnotations()); + if (methodChecked != null && checked == null) { + checked = methodChecked; } } } } - return match; + return (checked == Boolean.TRUE || + (checked == null && ((RootBeanDefinition) bdHolder.getBeanDefinition()).isDefaultCandidate())); } /** * Match the given qualifier annotations against the candidate bean definition. + * @return {@code false} if a qualifier has been found but not matched, + * {@code true} if a qualifier has been found and matched, + * {@code null} if no qualifier has been found at all */ - protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) { + + @Nullable + protected Boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) { boolean qualifierFound = false; if (!ObjectUtils.isEmpty(annotationsToSearch)) { SimpleTypeConverter typeConverter = new SimpleTypeConverter(); @@ -217,7 +226,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa } } } - return (qualifierFound || ((RootBeanDefinition) bdHolder.getBeanDefinition()).isDefaultCandidate()); + return (qualifierFound ? true : null); } /** 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 67bb4bc3b8..ed0ef504e8 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 @@ -195,6 +195,28 @@ class BeanMethodQualificationTests { ctx.close(); } + @Test + void customWithConstructor() { + AnnotationConfigApplicationContext ctx = context(CustomConfig.class, CustomPojoWithConstructor.class); + + CustomPojoWithConstructor pojo = ctx.getBean(CustomPojoWithConstructor.class); + assertThat(pojo.plainBean).isNull(); + assertThat(pojo.testBean.getName()).isEqualTo("interesting"); + + ctx.close(); + } + + @Test + void customWithMethod() { + AnnotationConfigApplicationContext ctx = context(CustomConfig.class, CustomPojoWithMethod.class); + + CustomPojoWithMethod pojo = ctx.getBean(CustomPojoWithMethod.class); + assertThat(pojo.plainBean).isNull(); + assertThat(pojo.testBean.getName()).isEqualTo("interesting"); + + ctx.close(); + } + @Test void beanNamesForAnnotation() { AnnotationConfigApplicationContext ctx = context(StandardConfig.class); @@ -327,6 +349,7 @@ class BeanMethodQualificationTests { } } + @Configuration static class EffectivePrimaryConfig { @@ -346,6 +369,7 @@ class BeanMethodQualificationTests { } } + @Component @Lazy static class StandardPojo { @@ -418,6 +442,35 @@ class BeanMethodQualificationTests { } + @InterestingPojo + static class CustomPojoWithConstructor { + + TestBean plainBean; + + TestBean testBean; + + public CustomPojoWithConstructor(Optional plainBean, @InterestingNeed TestBean testBean) { + this.plainBean = plainBean.orElse(null); + this.testBean = testBean; + } + } + + + @InterestingPojo + static class CustomPojoWithMethod { + + TestBean plainBean; + + TestBean testBean; + + @Autowired + public void applyDependencies(Optional plainBean, @InterestingNeed TestBean testBean) { + this.plainBean = plainBean.orElse(null); + this.testBean = testBean; + } + } + + @Qualifier @Retention(RetentionPolicy.RUNTIME) @interface Boring {