diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 6e0de9da543..24348a08feb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -574,7 +574,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto if (!matchFound) { // In case of FactoryBean, try to match FactoryBean instance itself next. beanName = FACTORY_BEAN_PREFIX + beanName; - matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); + if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) { + matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); + } } } if (matchFound) { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index accf86ef058..d492f28d941 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -23,7 +23,6 @@ import java.net.MalformedURLException; import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -89,6 +88,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatNoException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -172,10 +172,8 @@ class DefaultListableBeanFactoryTests { registerBeanDefinitions(p); assertThat(!DummyFactory.wasPrototypeCreated()).as("prototype not instantiated").isTrue(); - String[] beanNames = lbf.getBeanNamesForType(TestBean.class, true, false); - assertThat(beanNames).hasSize(0); - beanNames = lbf.getBeanNamesForAnnotation(SuppressWarnings.class); - assertThat(beanNames).hasSize(0); + assertBeanNamesForType(TestBean.class, false, false); + assertThat(lbf.getBeanNamesForAnnotation(SuppressWarnings.class)).isEmpty(); assertThat(lbf.containsSingleton("x1")).isFalse(); assertThat(lbf.containsBean("x1")).isTrue(); @@ -206,10 +204,8 @@ class DefaultListableBeanFactoryTests { registerBeanDefinitions(p); assertThat(!DummyFactory.wasPrototypeCreated()).as("prototype not instantiated").isTrue(); - String[] beanNames = lbf.getBeanNamesForType(TestBean.class, true, false); - assertThat(beanNames).hasSize(0); - beanNames = lbf.getBeanNamesForAnnotation(SuppressWarnings.class); - assertThat(beanNames).hasSize(0); + assertBeanNamesForType(TestBean.class, false, false); + assertThat(lbf.getBeanNamesForAnnotation(SuppressWarnings.class)).isEmpty(); assertThat(lbf.containsSingleton("x1")).isFalse(); assertThat(lbf.containsBean("x1")).isTrue(); @@ -239,10 +235,8 @@ class DefaultListableBeanFactoryTests { registerBeanDefinitions(p); assertThat(!DummyFactory.wasPrototypeCreated()).as("prototype not instantiated").isTrue(); - String[] beanNames = lbf.getBeanNamesForType(TestBean.class, true, false); - assertThat(beanNames).hasSize(0); - beanNames = lbf.getBeanNamesForAnnotation(SuppressWarnings.class); - assertThat(beanNames).hasSize(0); + assertBeanNamesForType(TestBean.class, false, false); + assertThat(lbf.getBeanNamesForAnnotation(SuppressWarnings.class)).isEmpty(); assertThat(lbf.containsSingleton("x1")).isFalse(); assertThat(lbf.containsBean("x1")).isTrue(); @@ -272,10 +266,8 @@ class DefaultListableBeanFactoryTests { registerBeanDefinitions(p); lbf.preInstantiateSingletons(); - assertThat(!DummyFactory.wasPrototypeCreated()).as("prototype not instantiated").isTrue(); - String[] beanNames = lbf.getBeanNamesForType(TestBean.class, true, false); - assertThat(beanNames).hasSize(1); - assertThat(beanNames[0]).isEqualTo("x1"); + assertThat(DummyFactory.wasPrototypeCreated()).as("prototype not instantiated").isFalse(); + assertBeanNamesForType(TestBean.class, true, false, "x1"); assertThat(lbf.containsSingleton("x1")).isTrue(); assertThat(lbf.containsBean("x1")).isTrue(); assertThat(lbf.containsBean("&x1")).isTrue(); @@ -310,14 +302,10 @@ class DefaultListableBeanFactoryTests { assertThat(lbf.isTypeMatch("&x2", Object.class)).isTrue(); assertThat(lbf.getType("x2")).isEqualTo(TestBean.class); assertThat(lbf.getType("&x2")).isEqualTo(DummyFactory.class); - assertThat(lbf.getAliases("x1").length).isEqualTo(1); - assertThat(lbf.getAliases("x1")[0]).isEqualTo("x2"); - assertThat(lbf.getAliases("&x1").length).isEqualTo(1); - assertThat(lbf.getAliases("&x1")[0]).isEqualTo("&x2"); - assertThat(lbf.getAliases("x2").length).isEqualTo(1); - assertThat(lbf.getAliases("x2")[0]).isEqualTo("x1"); - assertThat(lbf.getAliases("&x2").length).isEqualTo(1); - assertThat(lbf.getAliases("&x2")[0]).isEqualTo("&x1"); + assertThat(lbf.getAliases("x1")).containsExactly("x2"); + assertThat(lbf.getAliases("&x1")).containsExactly("&x2"); + assertThat(lbf.getAliases("x2")).containsExactly("x1"); + assertThat(lbf.getAliases("&x2")).containsExactly("&x1"); } @Test @@ -327,9 +315,7 @@ class DefaultListableBeanFactoryTests { lbf.registerBeanDefinition("x1", rbd); TestBeanFactory.initialized = false; - String[] beanNames = lbf.getBeanNamesForType(TestBean.class, true, false); - assertThat(beanNames).hasSize(1); - assertThat(beanNames[0]).isEqualTo("x1"); + assertBeanNamesForType(TestBean.class, true, false, "x1"); assertThat(lbf.containsSingleton("x1")).isFalse(); assertThat(lbf.containsBean("x1")).isTrue(); assertThat(lbf.containsBean("&x1")).isFalse(); @@ -352,9 +338,7 @@ class DefaultListableBeanFactoryTests { lbf.registerBeanDefinition("x1", rbd); TestBeanFactory.initialized = false; - String[] beanNames = lbf.getBeanNamesForType(TestBean.class, true, false); - assertThat(beanNames).hasSize(1); - assertThat(beanNames[0]).isEqualTo("x1"); + assertBeanNamesForType(TestBean.class, true, false, "x1"); assertThat(lbf.containsSingleton("x1")).isFalse(); assertThat(lbf.containsBean("x1")).isTrue(); assertThat(lbf.containsBean("&x1")).isFalse(); @@ -379,9 +363,7 @@ class DefaultListableBeanFactoryTests { lbf.registerBeanDefinition("x1", rbd); TestBeanFactory.initialized = false; - String[] beanNames = lbf.getBeanNamesForType(TestBean.class, true, false); - assertThat(beanNames).hasSize(1); - assertThat(beanNames[0]).isEqualTo("x1"); + assertBeanNamesForType(TestBean.class, true, false, "x1"); assertThat(lbf.containsSingleton("x1")).isFalse(); assertThat(lbf.containsBean("x1")).isTrue(); assertThat(lbf.containsBean("&x1")).isFalse(); @@ -407,9 +389,7 @@ class DefaultListableBeanFactoryTests { lbf.registerBeanDefinition("x1", rbd); TestBeanFactory.initialized = false; - String[] beanNames = lbf.getBeanNamesForType(TestBean.class, true, false); - assertThat(beanNames).hasSize(1); - assertThat(beanNames[0]).isEqualTo("x1"); + assertBeanNamesForType(TestBean.class, true, false, "x1"); assertThat(lbf.containsSingleton("x1")).isFalse(); assertThat(lbf.containsBean("x1")).isTrue(); assertThat(lbf.containsBean("&x1")).isFalse(); @@ -442,14 +422,10 @@ class DefaultListableBeanFactoryTests { assertThat(lbf.isTypeMatch("&x2", Object.class)).isFalse(); assertThat(lbf.getType("x2")).isEqualTo(TestBean.class); assertThat(lbf.getType("&x2")).isNull(); - assertThat(lbf.getAliases("x1").length).isEqualTo(1); - assertThat(lbf.getAliases("x1")[0]).isEqualTo("x2"); - assertThat(lbf.getAliases("&x1").length).isEqualTo(1); - assertThat(lbf.getAliases("&x1")[0]).isEqualTo("&x2"); - assertThat(lbf.getAliases("x2").length).isEqualTo(1); - assertThat(lbf.getAliases("x2")[0]).isEqualTo("x1"); - assertThat(lbf.getAliases("&x2").length).isEqualTo(1); - assertThat(lbf.getAliases("&x2")[0]).isEqualTo("&x1"); + assertThat(lbf.getAliases("x1")).containsExactly("x2"); + assertThat(lbf.getAliases("&x1")).containsExactly("&x2"); + assertThat(lbf.getAliases("x2")).containsExactly("x1"); + assertThat(lbf.getAliases("&x2")).containsExactly("&x1"); } @Test @@ -613,8 +589,7 @@ class DefaultListableBeanFactoryTests { lbf.registerSingleton("string", "A"); TestBean self = (TestBean) lbf.getBean("self"); - assertThat(self.getStringArray()).hasSize(1); - assertThat(self.getStringArray()).contains("A"); + assertThat(self.getStringArray()).containsExactly("A"); } @Test @@ -627,8 +602,7 @@ class DefaultListableBeanFactoryTests { lbf.registerSingleton("string", "A"); TestBean self = (TestBean) lbf.getBean("self"); - assertThat(self.getStringArray()).hasSize(1); - assertThat(self.getStringArray()).contains("A"); + assertThat(self.getStringArray()).containsExactly("A"); } @Test @@ -660,8 +634,7 @@ class DefaultListableBeanFactoryTests { .withCauseInstanceOf(NotWritablePropertyException.class) .satisfies(ex -> { NotWritablePropertyException cause = (NotWritablePropertyException) ex.getCause(); - assertThat(cause.getPossibleMatches()).hasSize(1); - assertThat(cause.getPossibleMatches()[0]).isEqualTo("age"); + assertThat(cause.getPossibleMatches()).containsExactly("age"); }); } @@ -679,7 +652,7 @@ class DefaultListableBeanFactoryTests { lbf = new DefaultListableBeanFactory(); p = new Properties(); p.setProperty("kerry.(class)", TestBean.class.getName()); - p.setProperty("kerry.(scope)", "prototype"); + p.setProperty("kerry.(scope)", BeanDefinition.SCOPE_PROTOTYPE); p.setProperty("kerry.age", "35"); registerBeanDefinitions(p); kerry1 = (TestBean) lbf.getBean("kerry"); @@ -1150,7 +1123,7 @@ class DefaultListableBeanFactoryTests { assertThat(lbf.containsBean("singletonObject")).isTrue(); assertThat(lbf.isSingleton("singletonObject")).isTrue(); assertThat(lbf.getType("singletonObject")).isEqualTo(TestBean.class); - assertThat(lbf.getAliases("singletonObject").length).isEqualTo(0); + assertThat(lbf.getAliases("singletonObject")).isEmpty(); DependenciesBean test = (DependenciesBean) lbf.getBean("test"); assertThat(lbf.getBean("singletonObject")).isEqualTo(singletonObject); assertThat(test.getSpouse()).isEqualTo(singletonObject); @@ -1796,12 +1769,12 @@ class DefaultListableBeanFactoryTests { assertThat(bean.beanName).isEqualTo("bd1"); assertThat(bean.spouseAge).isEqualTo(42); - assertThat(lbf.getBeanNamesForType(ConstructorDependency.class).length).isEqualTo(1); - assertThat(lbf.getBeanNamesForType(ConstructorDependencyFactoryBean.class).length).isEqualTo(1); - assertThat(lbf.getBeanNamesForType(ResolvableType.forClassWithGenerics(FactoryBean.class, Object.class)).length).isEqualTo(1); - assertThat(lbf.getBeanNamesForType(ResolvableType.forClassWithGenerics(FactoryBean.class, String.class)).length).isEqualTo(0); - assertThat(lbf.getBeanNamesForType(ResolvableType.forClassWithGenerics(FactoryBean.class, Object.class), true, true).length).isEqualTo(1); - assertThat(lbf.getBeanNamesForType(ResolvableType.forClassWithGenerics(FactoryBean.class, String.class), true, true).length).isEqualTo(0); + assertThat(lbf.getBeanNamesForType(ConstructorDependency.class)).hasSize(1); + assertThat(lbf.getBeanNamesForType(ConstructorDependencyFactoryBean.class)).hasSize(1); + assertThat(lbf.getBeanNamesForType(ResolvableType.forClassWithGenerics(FactoryBean.class, Object.class))).hasSize(1); + assertThat(lbf.getBeanNamesForType(ResolvableType.forClassWithGenerics(FactoryBean.class, String.class))).isEmpty(); + assertThat(lbf.getBeanNamesForType(ResolvableType.forClassWithGenerics(FactoryBean.class, Object.class), true, true)).hasSize(1); + assertThat(lbf.getBeanNamesForType(ResolvableType.forClassWithGenerics(FactoryBean.class, String.class), true, true)).isEmpty(); } private RootBeanDefinition createConstructorDependencyBeanDefinition(int age) { @@ -1866,46 +1839,45 @@ class DefaultListableBeanFactoryTests { @Test void getBeanNamesForTypeBeforeFactoryBeanCreation() { + FactoryBeanThatShouldntBeCalled.instantiated = false; lbf.registerBeanDefinition("factoryBean", new RootBeanDefinition(FactoryBeanThatShouldntBeCalled.class)); assertThat(lbf.containsSingleton("factoryBean")).isFalse(); + assertThat(FactoryBeanThatShouldntBeCalled.instantiated).isFalse(); - String[] beanNames = lbf.getBeanNamesForType(Runnable.class, false, false); - assertThat(beanNames.length).isEqualTo(1); - assertThat(beanNames[0]).isEqualTo("&factoryBean"); - - beanNames = lbf.getBeanNamesForType(Callable.class, false, false); - assertThat(beanNames.length).isEqualTo(1); - assertThat(beanNames[0]).isEqualTo("&factoryBean"); - - beanNames = lbf.getBeanNamesForType(RepositoryFactoryInformation.class, false, false); - assertThat(beanNames.length).isEqualTo(1); - assertThat(beanNames[0]).isEqualTo("&factoryBean"); - - beanNames = lbf.getBeanNamesForType(FactoryBean.class, false, false); - assertThat(beanNames.length).isEqualTo(1); - assertThat(beanNames[0]).isEqualTo("&factoryBean"); + assertBeanNamesForType(Runnable.class, false, false, "&factoryBean"); + assertBeanNamesForType(Callable.class, false, false, "&factoryBean"); + assertBeanNamesForType(RepositoryFactoryInformation.class, false, false, "&factoryBean"); + assertBeanNamesForType(FactoryBean.class, false, false, "&factoryBean"); } @Test void getBeanNamesForTypeAfterFactoryBeanCreation() { + FactoryBeanThatShouldntBeCalled.instantiated = false; lbf.registerBeanDefinition("factoryBean", new RootBeanDefinition(FactoryBeanThatShouldntBeCalled.class)); lbf.getBean("&factoryBean"); + assertThat(FactoryBeanThatShouldntBeCalled.instantiated).isTrue(); + assertThat(lbf.containsSingleton("factoryBean")).isTrue(); + + assertBeanNamesForType(Runnable.class, false, false, "&factoryBean"); + assertBeanNamesForType(Callable.class, false, false, "&factoryBean"); + assertBeanNamesForType(RepositoryFactoryInformation.class, false, false, "&factoryBean"); + assertBeanNamesForType(FactoryBean.class, false, false, "&factoryBean"); + } + + @Test // gh-28616 + void getBeanNamesForTypeWithPrototypeScopedFactoryBean() { + FactoryBeanThatShouldntBeCalled.instantiated = false; + RootBeanDefinition beanDefinition = new RootBeanDefinition(FactoryBeanThatShouldntBeCalled.class); + beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE); + lbf.registerBeanDefinition("factoryBean", beanDefinition); + assertThat(FactoryBeanThatShouldntBeCalled.instantiated).isFalse(); + assertThat(lbf.containsSingleton("factoryBean")).isFalse(); - String[] beanNames = lbf.getBeanNamesForType(Runnable.class, false, false); - assertThat(beanNames.length).isEqualTo(1); - assertThat(beanNames[0]).isEqualTo("&factoryBean"); - - beanNames = lbf.getBeanNamesForType(Callable.class, false, false); - assertThat(beanNames.length).isEqualTo(1); - assertThat(beanNames[0]).isEqualTo("&factoryBean"); - - beanNames = lbf.getBeanNamesForType(RepositoryFactoryInformation.class, false, false); - assertThat(beanNames.length).isEqualTo(1); - assertThat(beanNames[0]).isEqualTo("&factoryBean"); - - beanNames = lbf.getBeanNamesForType(FactoryBean.class, false, false); - assertThat(beanNames.length).isEqualTo(1); - assertThat(beanNames[0]).isEqualTo("&factoryBean"); + // We should not find any beans of the following types if the FactoryBean itself is prototype-scoped. + assertBeanNamesForType(Runnable.class, false, false); + assertBeanNamesForType(Callable.class, false, false); + assertBeanNamesForType(RepositoryFactoryInformation.class, false, false); + assertBeanNamesForType(FactoryBean.class, false, false); } /** @@ -2170,8 +2142,7 @@ class DefaultListableBeanFactoryTests { RootBeanDefinition bd = new RootBeanDefinition(ConstructorDependencyBean.class); bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); lbf.registerBeanDefinition("test", bd); - assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy( - lbf::preInstantiateSingletons); + assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(lbf::preInstantiateSingletons); } @Test @@ -2179,8 +2150,7 @@ class DefaultListableBeanFactoryTests { RootBeanDefinition bd = new RootBeanDefinition(ConstructorDependencyFactoryBean.class); bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); lbf.registerBeanDefinition("test", bd); - assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy( - lbf::preInstantiateSingletons); + assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(lbf::preInstantiateSingletons); } @Test @@ -2188,8 +2158,7 @@ class DefaultListableBeanFactoryTests { RootBeanDefinition bd = new RootBeanDefinition(ConstructorDependencyFactoryBean.class); bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); lbf.registerBeanDefinition("test", bd); - assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() -> - lbf.getBeansOfType(String.class)); + assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() -> lbf.getBeansOfType(String.class)); } @Test @@ -2216,8 +2185,7 @@ class DefaultListableBeanFactoryTests { RootBeanDefinition bd = new RootBeanDefinition(ConstructorDependencyWithClassResolution.class); bd.getConstructorArgumentValues().addGenericArgumentValue("java.lang.Strin"); lbf.registerBeanDefinition("test", bd); - assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy( - lbf::preInstantiateSingletons); + assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(lbf::preInstantiateSingletons); } @Test @@ -2241,7 +2209,13 @@ class DefaultListableBeanFactoryTests { @Test void prototypeFactoryBeanNotEagerlyCalled() { lbf.registerBeanDefinition("test", new RootBeanDefinition(FactoryBeanThatShouldntBeCalled.class)); - lbf.preInstantiateSingletons(); + assertThatNoException().isThrownBy(lbf::preInstantiateSingletons); + } + + @Test + void prototypeFactoryBeanNotEagerlyCalledInCaseOfBeanClassName() { + lbf.registerBeanDefinition("test", new RootBeanDefinition(FactoryBeanThatShouldntBeCalled.class.getName(), null, null)); + assertThatNoException().isThrownBy(lbf::preInstantiateSingletons); } @Test @@ -2281,13 +2255,6 @@ class DefaultListableBeanFactoryTests { assertThat(factory.initialized).isTrue(); } - @Test - void prototypeFactoryBeanNotEagerlyCalledInCaseOfBeanClassName() { - lbf.registerBeanDefinition("test", - new RootBeanDefinition(FactoryBeanThatShouldntBeCalled.class.getName(), null, null)); - lbf.preInstantiateSingletons(); - } - @Test void prototypeStringCreatedRepeatedly() { RootBeanDefinition stringDef = new RootBeanDefinition(String.class); @@ -2480,10 +2447,7 @@ class DefaultListableBeanFactoryTests { lbf.registerBeanDefinition("fmWithArgs", factoryMethodDefinitionWithArgs); assertThat(lbf.getBeanDefinitionCount()).isEqualTo(4); - List tbNames = Arrays.asList(lbf.getBeanNamesForType(TestBean.class)); - assertThat(tbNames.contains("fmWithProperties")).isTrue(); - assertThat(tbNames.contains("fmWithArgs")).isTrue(); - assertThat(tbNames.size()).isEqualTo(2); + assertBeanNamesForType(TestBean.class, true, true, "fmWithProperties", "fmWithArgs"); TestBean tb = (TestBean) lbf.getBean("fmWithProperties"); TestBean second = (TestBean) lbf.getBean("fmWithProperties"); @@ -2671,6 +2635,19 @@ class DefaultListableBeanFactoryTests { return (new org.springframework.beans.factory.support.PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p, prefix); } + private void assertBeanNamesForType(Class type, boolean includeNonSingletons, boolean allowEagerInit, String... names) { + if (names.length == 0) { + assertThat(lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit)) + .as("bean names for type " + type.getName()) + .isEmpty(); + } + else { + assertThat(lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit)) + .as("bean names for type " + type.getName()) + .containsExactly(names); + } + } + public static class NoDependencies { @@ -2894,6 +2871,12 @@ class DefaultListableBeanFactoryTests { public static class FactoryBeanThatShouldntBeCalled, S, ID extends Serializable> extends RepositoryFactoryBeanSupport implements Runnable, Callable { + static boolean instantiated = false; + + { + instantiated = true; + } + @Override public T getObject() { throw new IllegalStateException();