Introduce defaultCandidate flag (for plain type vs. qualified match)
Closes gh-26528
This commit is contained in:
parent
bc2257aaff
commit
a8fb16b47c
|
@ -167,12 +167,14 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
|||
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;
|
||||
}
|
||||
|
@ -185,6 +187,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
|||
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.
|
||||
|
@ -199,7 +202,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
|||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return (qualifierFound || ((RootBeanDefinition) bdHolder.getBeanDefinition()).isDefaultCandidate());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
|
@ -185,6 +185,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
|
||||
private boolean autowireCandidate = true;
|
||||
|
||||
private boolean defaultCandidate = true;
|
||||
|
||||
private boolean primary = false;
|
||||
|
||||
private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
|
||||
|
@ -284,6 +286,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
setDependencyCheck(originalAbd.getDependencyCheck());
|
||||
setDependsOn(originalAbd.getDependsOn());
|
||||
setAutowireCandidate(originalAbd.isAutowireCandidate());
|
||||
setDefaultCandidate(originalAbd.isDefaultCandidate());
|
||||
setPrimary(originalAbd.isPrimary());
|
||||
copyQualifiersFrom(originalAbd);
|
||||
setInstanceSupplier(originalAbd.getInstanceSupplier());
|
||||
|
@ -360,6 +363,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
setDependencyCheck(otherAbd.getDependencyCheck());
|
||||
setDependsOn(otherAbd.getDependsOn());
|
||||
setAutowireCandidate(otherAbd.isAutowireCandidate());
|
||||
setDefaultCandidate(otherAbd.isDefaultCandidate());
|
||||
setPrimary(otherAbd.isPrimary());
|
||||
copyQualifiersFrom(otherAbd);
|
||||
setInstanceSupplier(otherAbd.getInstanceSupplier());
|
||||
|
@ -686,7 +690,10 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
}
|
||||
|
||||
/**
|
||||
* Set whether this bean is a candidate for getting autowired into some other bean.
|
||||
* Set whether this bean is a candidate for getting autowired into some other
|
||||
* bean at all.
|
||||
* <p>Default is {@code true}, allowing injection by type at any injection point.
|
||||
* Switch this to {@code false} in order to disable autowiring by type for this bean.
|
||||
* <p>Note that this flag is designed to only affect type-based autowiring.
|
||||
* It does not affect explicit references by name, which will get resolved even
|
||||
* if the specified bean is not marked as an autowire candidate. As a consequence,
|
||||
|
@ -700,17 +707,41 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
}
|
||||
|
||||
/**
|
||||
* Return whether this bean is a candidate for getting autowired into some other bean.
|
||||
* Return whether this bean is a candidate for getting autowired into some other
|
||||
* bean at all.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAutowireCandidate() {
|
||||
return this.autowireCandidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this bean is a candidate for getting autowired into some other
|
||||
* bean based on the plain type, without any further indications such as a
|
||||
* qualifier match.
|
||||
* <p>Default is {@code true}, allowing injection by type at any injection point.
|
||||
* Switch this to {@code false} in order to restrict injection by default,
|
||||
* effectively enforcing an additional indication such as a qualifier match.
|
||||
* @since 6.2
|
||||
*/
|
||||
public void setDefaultCandidate(boolean defaultCandidate) {
|
||||
this.defaultCandidate = defaultCandidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this bean is a candidate for getting autowired into some other
|
||||
* bean based on the plain type, without any further indications such as a
|
||||
* qualifier match?
|
||||
* @since 6.2
|
||||
*/
|
||||
public boolean isDefaultCandidate() {
|
||||
return this.defaultCandidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this bean is a primary autowire candidate.
|
||||
* <p>If this value is {@code true} for exactly one bean among multiple
|
||||
* matching candidates, it will serve as a tie-breaker.
|
||||
* <p>Default is {@code false}. If this value is {@code true} for exactly one
|
||||
* bean among multiple matching candidates, it will serve as a tie-breaker.
|
||||
*/
|
||||
@Override
|
||||
public void setPrimary(boolean primary) {
|
||||
|
|
|
@ -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.
|
||||
|
@ -239,13 +239,27 @@ public @interface Bean {
|
|||
String[] name() default {};
|
||||
|
||||
/**
|
||||
* Is this bean a candidate for getting autowired into some other bean?
|
||||
* Is this bean a candidate for getting autowired into some other bean at all?
|
||||
* <p>Default is {@code true}; set this to {@code false} for internal delegates
|
||||
* that are not meant to get in the way of beans of the same type in other places.
|
||||
* @since 5.1
|
||||
* @see #defaultCandidate()
|
||||
*/
|
||||
boolean autowireCandidate() default true;
|
||||
|
||||
/**
|
||||
* Is this bean a candidate for getting autowired into some other bean based on
|
||||
* the plain type, without any further indications such as a qualifier match?
|
||||
* <p>Default is {@code true}; set this to {@code false} for restricted delegates
|
||||
* that are supposed to be injectable in certain areas but are not meant to get
|
||||
* in the way of beans of the same type in other places.
|
||||
* <p>This is a variation of {@link #autowireCandidate()} which does not disable
|
||||
* injection in general, just enforces an additional indication such as a qualifier.
|
||||
* @since 6.2
|
||||
* @see #autowireCandidate()
|
||||
*/
|
||||
boolean defaultCandidate() default true;
|
||||
|
||||
/**
|
||||
* The optional name of a method to call on the bean instance during initialization.
|
||||
* Not commonly used, given that the method may be called programmatically directly
|
||||
|
|
|
@ -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.
|
||||
|
@ -241,6 +241,11 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
beanDef.setAutowireCandidate(false);
|
||||
}
|
||||
|
||||
boolean defaultCandidate = bean.getBoolean("defaultCandidate");
|
||||
if (!defaultCandidate) {
|
||||
beanDef.setDefaultCandidate(false);
|
||||
}
|
||||
|
||||
String initMethodName = bean.getString("initMethod");
|
||||
if (StringUtils.hasText(initMethodName)) {
|
||||
beanDef.setInitMethodName(initMethodName);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -89,6 +89,7 @@ class BeanMethodQualificationTests {
|
|||
assertThat(BeanFactoryAnnotationUtils.isQualifierMatch(value -> value.equals("boring"),
|
||||
"testBean2", ctx.getDefaultListableBeanFactory())).isTrue();
|
||||
CustomPojo pojo = ctx.getBean(CustomPojo.class);
|
||||
assertThat(pojo.plainBean).isNull();
|
||||
assertThat(pojo.testBean.getName()).isEqualTo("interesting");
|
||||
TestBean testBean2 = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
|
||||
ctx.getDefaultListableBeanFactory(), TestBean.class, "boring");
|
||||
|
@ -132,7 +133,9 @@ class BeanMethodQualificationTests {
|
|||
new AnnotationConfigApplicationContext(CustomConfigWithAttributeOverride.class, CustomPojo.class);
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("testBeanX")).isFalse();
|
||||
CustomPojo pojo = ctx.getBean(CustomPojo.class);
|
||||
assertThat(pojo.plainBean).isNull();
|
||||
assertThat(pojo.testBean.getName()).isEqualTo("interesting");
|
||||
assertThat(pojo.nestedTestBean).isNull();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
@ -219,7 +222,7 @@ class BeanMethodQualificationTests {
|
|||
return new TestBean("interesting");
|
||||
}
|
||||
|
||||
@Bean @Qualifier("boring") @Lazy
|
||||
@Bean(defaultCandidate=false) @Qualifier("boring") @Lazy
|
||||
public TestBean testBean2(@Lazy TestBean testBean1) {
|
||||
TestBean tb = new TestBean("boring");
|
||||
tb.setSpouse(testBean1);
|
||||
|
@ -235,7 +238,7 @@ class BeanMethodQualificationTests {
|
|||
return new TestBean("interesting");
|
||||
}
|
||||
|
||||
@Bean @Qualifier("boring")
|
||||
@Bean(defaultCandidate=false) @Qualifier("boring")
|
||||
public TestBean testBean2(@Lazy TestBean testBean1) {
|
||||
TestBean tb = new TestBean("boring");
|
||||
tb.setSpouse(testBean1);
|
||||
|
@ -246,17 +249,19 @@ class BeanMethodQualificationTests {
|
|||
@InterestingPojo
|
||||
static class CustomPojo {
|
||||
|
||||
@Autowired(required=false) TestBean plainBean;
|
||||
|
||||
@InterestingNeed TestBean testBean;
|
||||
|
||||
@InterestingNeedWithRequiredOverride(required=false) NestedTestBean nestedTestBean;
|
||||
}
|
||||
|
||||
@Bean @Lazy @Qualifier("interesting")
|
||||
@Bean(defaultCandidate=false) @Lazy @Qualifier("interesting")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface InterestingBean {
|
||||
}
|
||||
|
||||
@Bean @Lazy @Qualifier("interesting")
|
||||
@Bean(defaultCandidate=false) @Lazy @Qualifier("interesting")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface InterestingBeanWithName {
|
||||
|
||||
|
|
Loading…
Reference in New Issue