Clarify primary/fallback autowiring for arrays/collections/maps

Closes gh-32301
This commit is contained in:
Juergen Hoeller
2024-02-21 20:24:57 +01:00
parent f5397d6426
commit f811f0dc18
3 changed files with 84 additions and 27 deletions

View File

@@ -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 {