Clarify primary/fallback autowiring for arrays/collections/maps
Closes gh-32301
This commit is contained in:
@@ -18,9 +18,13 @@ package org.springframework.context.annotation.configuration;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||
@@ -38,6 +42,7 @@ import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -47,6 +52,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Yanming Zhou
|
||||
*/
|
||||
class BeanMethodQualificationTests {
|
||||
|
||||
@@ -54,10 +60,12 @@ class BeanMethodQualificationTests {
|
||||
void standard() {
|
||||
AnnotationConfigApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(StandardConfig.class, StandardPojo.class);
|
||||
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("testBean1")).isFalse();
|
||||
StandardPojo pojo = ctx.getBean(StandardPojo.class);
|
||||
assertThat(pojo.testBean.getName()).isEqualTo("interesting");
|
||||
assertThat(pojo.testBean2.getName()).isEqualTo("boring");
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -65,10 +73,12 @@ class BeanMethodQualificationTests {
|
||||
void scoped() {
|
||||
AnnotationConfigApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(ScopedConfig.class, StandardPojo.class);
|
||||
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("testBean1")).isFalse();
|
||||
StandardPojo pojo = ctx.getBean(StandardPojo.class);
|
||||
assertThat(pojo.testBean.getName()).isEqualTo("interesting");
|
||||
assertThat(pojo.testBean2.getName()).isEqualTo("boring");
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -76,36 +86,37 @@ class BeanMethodQualificationTests {
|
||||
void scopedProxy() {
|
||||
AnnotationConfigApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(ScopedProxyConfig.class, StandardPojo.class);
|
||||
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("testBean1")).isTrue(); // a shared scoped proxy
|
||||
StandardPojo pojo = ctx.getBean(StandardPojo.class);
|
||||
assertThat(pojo.testBean.getName()).isEqualTo("interesting");
|
||||
assertThat(pojo.testBean2.getName()).isEqualTo("boring");
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void primary() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@ParameterizedTest
|
||||
@ValueSource(classes = {PrimaryConfig.class, FallbackConfig.class})
|
||||
void primaryVersusFallback(Class<?> configClass) {
|
||||
AnnotationConfigApplicationContext ctx =
|
||||
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();
|
||||
}
|
||||
new AnnotationConfigApplicationContext(configClass, StandardPojo.class, ConstructorPojo.class);
|
||||
|
||||
@Test
|
||||
void fallback() {
|
||||
AnnotationConfigApplicationContext ctx =
|
||||
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");
|
||||
assertThat(pojo.testBean2.getSpouse().getName()).isEqualTo("interesting");
|
||||
assertThat((List<Object>) pojo.testBean2.getPets()).contains(
|
||||
ctx.getBean("testBean1x"), ctx.getBean("testBean2x")); // array injection
|
||||
assertThat((List<Object>) pojo.testBean2.getSomeList()).contains(
|
||||
ctx.getBean("testBean1x"), ctx.getBean("testBean2x")); // list injection
|
||||
assertThat((Map<String, TestBean>) pojo.testBean2.getSomeMap()).containsKeys(
|
||||
"testBean1x", "testBean2x"); // map injection
|
||||
|
||||
ConstructorPojo pojo2 = ctx.getBean(ConstructorPojo.class);
|
||||
assertThat(pojo2.testBean.getName()).isEqualTo("interesting");
|
||||
assertThat(pojo2.testBean2.getName()).isEqualTo("boring");
|
||||
assertThat(pojo2.testBean).isSameAs(pojo.testBean);
|
||||
assertThat(pojo2.testBean2).isSameAs(pojo.testBean2);
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -113,16 +124,20 @@ class BeanMethodQualificationTests {
|
||||
void customWithLazyResolution() {
|
||||
AnnotationConfigApplicationContext ctx =
|
||||
new AnnotationConfigApplicationContext(CustomConfig.class, CustomPojo.class);
|
||||
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("testBean1")).isFalse();
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("testBean2")).isFalse();
|
||||
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");
|
||||
assertThat(testBean2.getName()).isEqualTo("boring");
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -131,13 +146,16 @@ class BeanMethodQualificationTests {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(CustomConfig.class, CustomPojo.class);
|
||||
ctx.refresh();
|
||||
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("testBean1")).isFalse();
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("testBean2")).isFalse();
|
||||
ctx.getBean("testBean2");
|
||||
assertThat(BeanFactoryAnnotationUtils.isQualifierMatch(value -> value.equals("boring"),
|
||||
"testBean2", ctx.getDefaultListableBeanFactory())).isTrue();
|
||||
|
||||
CustomPojo pojo = ctx.getBean(CustomPojo.class);
|
||||
assertThat(pojo.testBean.getName()).isEqualTo("interesting");
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -149,10 +167,12 @@ class BeanMethodQualificationTests {
|
||||
customPojo.setLazyInit(true);
|
||||
ctx.registerBeanDefinition("customPojo", customPojo);
|
||||
ctx.refresh();
|
||||
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("testBean1")).isFalse();
|
||||
assertThat(ctx.getBeanFactory().containsSingleton("testBean2")).isFalse();
|
||||
CustomPojo pojo = ctx.getBean(CustomPojo.class);
|
||||
assertThat(pojo.testBean.getName()).isEqualTo("interesting");
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -160,21 +180,29 @@ class BeanMethodQualificationTests {
|
||||
void customWithAttributeOverride() {
|
||||
AnnotationConfigApplicationContext ctx =
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanNamesForAnnotation() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(StandardConfig.class);
|
||||
assertThat(ctx.getBeanNamesForAnnotation(Configuration.class)).isEqualTo(new String[] {"beanMethodQualificationTests.StandardConfig"});
|
||||
assertThat(ctx.getBeanNamesForAnnotation(Scope.class)).isEqualTo(new String[] {});
|
||||
assertThat(ctx.getBeanNamesForAnnotation(Lazy.class)).isEqualTo(new String[] {"testBean1"});
|
||||
assertThat(ctx.getBeanNamesForAnnotation(Boring.class)).isEqualTo(new String[] {"beanMethodQualificationTests.StandardConfig", "testBean2"});
|
||||
|
||||
assertThat(ctx.getBeanNamesForAnnotation(Configuration.class)).isEqualTo(
|
||||
new String[] {"beanMethodQualificationTests.StandardConfig"});
|
||||
assertThat(ctx.getBeanNamesForAnnotation(Scope.class)).isEqualTo(
|
||||
new String[] {});
|
||||
assertThat(ctx.getBeanNamesForAnnotation(Lazy.class)).isEqualTo(
|
||||
new String[] {"testBean1"});
|
||||
assertThat(ctx.getBeanNamesForAnnotation(Boring.class)).isEqualTo(
|
||||
new String[] {"beanMethodQualificationTests.StandardConfig", "testBean2"});
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -196,6 +224,7 @@ class BeanMethodQualificationTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Boring
|
||||
static class ScopedConfig {
|
||||
@@ -213,6 +242,7 @@ class BeanMethodQualificationTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Boring
|
||||
static class ScopedProxyConfig {
|
||||
@@ -230,6 +260,7 @@ class BeanMethodQualificationTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class PrimaryConfig {
|
||||
|
||||
@@ -244,9 +275,14 @@ class BeanMethodQualificationTests {
|
||||
}
|
||||
|
||||
@Bean @Boring @Primary
|
||||
public TestBean testBean2(TestBean testBean1) {
|
||||
public TestBean testBean2(TestBean testBean1, TestBean[] testBeanArray,
|
||||
List<TestBean> testBeanList, Map<String, TestBean> testBeanMap) {
|
||||
|
||||
TestBean tb = new TestBean("boring");
|
||||
tb.setSpouse(testBean1);
|
||||
tb.setPets(CollectionUtils.arrayToList(testBeanArray));
|
||||
tb.setSomeList(testBeanList);
|
||||
tb.setSomeMap(testBeanMap);
|
||||
return tb;
|
||||
}
|
||||
|
||||
@@ -256,6 +292,7 @@ class BeanMethodQualificationTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class FallbackConfig {
|
||||
|
||||
@@ -270,9 +307,14 @@ class BeanMethodQualificationTests {
|
||||
}
|
||||
|
||||
@Bean @Boring
|
||||
public TestBean testBean2(TestBean testBean1) {
|
||||
public TestBean testBean2(TestBean testBean1, TestBean[] testBeanArray,
|
||||
List<TestBean> testBeanList, Map<String, TestBean> testBeanMap) {
|
||||
|
||||
TestBean tb = new TestBean("boring");
|
||||
tb.setSpouse(testBean1);
|
||||
tb.setPets(CollectionUtils.arrayToList(testBeanArray));
|
||||
tb.setSomeList(testBeanList);
|
||||
tb.setSomeMap(testBeanMap);
|
||||
return tb;
|
||||
}
|
||||
|
||||
@@ -282,6 +324,7 @@ class BeanMethodQualificationTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component @Lazy
|
||||
static class StandardPojo {
|
||||
|
||||
@@ -290,6 +333,7 @@ class BeanMethodQualificationTests {
|
||||
@Autowired @Boring TestBean testBean2;
|
||||
}
|
||||
|
||||
|
||||
@Component @Lazy
|
||||
static class ConstructorPojo {
|
||||
|
||||
@@ -303,10 +347,6 @@ class BeanMethodQualificationTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Qualifier
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Boring {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomConfig {
|
||||
@@ -324,6 +364,7 @@ class BeanMethodQualificationTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class CustomConfigWithAttributeOverride {
|
||||
|
||||
@@ -340,6 +381,7 @@ class BeanMethodQualificationTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@InterestingPojo
|
||||
static class CustomPojo {
|
||||
|
||||
@@ -354,6 +396,12 @@ class BeanMethodQualificationTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Qualifier
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Boring {
|
||||
}
|
||||
|
||||
@Bean(defaultCandidate=false) @Lazy @Qualifier("interesting")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface InterestingBean {
|
||||
|
||||
Reference in New Issue
Block a user