Skip to content

Commit ee1fe8a

Browse files
committed
Consistently evaluate defaultCandidate flag on constructors and methods
Closes gh-33762
1 parent 6f9413b commit ee1fe8a

File tree

2 files changed

+74
-12
lines changed

2 files changed

+74
-12
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -154,26 +154,35 @@ public void setValueAnnotationType(Class<? extends Annotation> valueAnnotationTy
154154
*/
155155
@Override
156156
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
157-
boolean match = super.isAutowireCandidate(bdHolder, descriptor);
158-
if (match) {
159-
match = checkQualifiers(bdHolder, descriptor.getAnnotations());
160-
if (match) {
161-
MethodParameter methodParam = descriptor.getMethodParameter();
162-
if (methodParam != null) {
163-
Method method = methodParam.getMethod();
164-
if (method == null || void.class == method.getReturnType()) {
165-
match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
157+
if (!super.isAutowireCandidate(bdHolder, descriptor)) {
158+
return false;
159+
}
160+
Boolean checked = checkQualifiers(bdHolder, descriptor.getAnnotations());
161+
if (checked != Boolean.FALSE) {
162+
MethodParameter methodParam = descriptor.getMethodParameter();
163+
if (methodParam != null) {
164+
Method method = methodParam.getMethod();
165+
if (method == null || void.class == method.getReturnType()) {
166+
Boolean methodChecked = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
167+
if (methodChecked != null && checked == null) {
168+
checked = methodChecked;
166169
}
167170
}
168171
}
169172
}
170-
return match;
173+
return (checked == Boolean.TRUE ||
174+
(checked == null && ((RootBeanDefinition) bdHolder.getBeanDefinition()).isDefaultCandidate()));
171175
}
172176

173177
/**
174178
* Match the given qualifier annotations against the candidate bean definition.
179+
* @return {@code false} if a qualifier has been found but not matched,
180+
* {@code true} if a qualifier has been found and matched,
181+
* {@code null} if no qualifier has been found at all
175182
*/
176-
protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
183+
184+
@Nullable
185+
protected Boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
177186
boolean qualifierFound = false;
178187
if (!ObjectUtils.isEmpty(annotationsToSearch)) {
179188
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
@@ -217,7 +226,7 @@ protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] an
217226
}
218227
}
219228
}
220-
return (qualifierFound || ((RootBeanDefinition) bdHolder.getBeanDefinition()).isDefaultCandidate());
229+
return (qualifierFound ? true : null);
221230
}
222231

223232
/**

spring-context/src/test/java/org/springframework/context/annotation/configuration/BeanMethodQualificationTests.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,28 @@ void customWithAttributeOverride() {
195195
ctx.close();
196196
}
197197

198+
@Test
199+
void customWithConstructor() {
200+
AnnotationConfigApplicationContext ctx = context(CustomConfig.class, CustomPojoWithConstructor.class);
201+
202+
CustomPojoWithConstructor pojo = ctx.getBean(CustomPojoWithConstructor.class);
203+
assertThat(pojo.plainBean).isNull();
204+
assertThat(pojo.testBean.getName()).isEqualTo("interesting");
205+
206+
ctx.close();
207+
}
208+
209+
@Test
210+
void customWithMethod() {
211+
AnnotationConfigApplicationContext ctx = context(CustomConfig.class, CustomPojoWithMethod.class);
212+
213+
CustomPojoWithMethod pojo = ctx.getBean(CustomPojoWithMethod.class);
214+
assertThat(pojo.plainBean).isNull();
215+
assertThat(pojo.testBean.getName()).isEqualTo("interesting");
216+
217+
ctx.close();
218+
}
219+
198220
@Test
199221
void beanNamesForAnnotation() {
200222
AnnotationConfigApplicationContext ctx = context(StandardConfig.class);
@@ -327,6 +349,7 @@ public TestBean testBean2x() {
327349
}
328350
}
329351

352+
330353
@Configuration
331354
static class EffectivePrimaryConfig {
332355

@@ -346,6 +369,7 @@ public TestBean fallback2() {
346369
}
347370
}
348371

372+
349373
@Component @Lazy
350374
static class StandardPojo {
351375

@@ -418,6 +442,35 @@ public CustomPojo(Optional<TestBean> plainBean) {
418442
}
419443

420444

445+
@InterestingPojo
446+
static class CustomPojoWithConstructor {
447+
448+
TestBean plainBean;
449+
450+
TestBean testBean;
451+
452+
public CustomPojoWithConstructor(Optional<TestBean> plainBean, @InterestingNeed TestBean testBean) {
453+
this.plainBean = plainBean.orElse(null);
454+
this.testBean = testBean;
455+
}
456+
}
457+
458+
459+
@InterestingPojo
460+
static class CustomPojoWithMethod {
461+
462+
TestBean plainBean;
463+
464+
TestBean testBean;
465+
466+
@Autowired
467+
public void applyDependencies(Optional<TestBean> plainBean, @InterestingNeed TestBean testBean) {
468+
this.plainBean = plainBean.orElse(null);
469+
this.testBean = testBean;
470+
}
471+
}
472+
473+
421474
@Qualifier
422475
@Retention(RetentionPolicy.RUNTIME)
423476
@interface Boring {

0 commit comments

Comments
 (0)