diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 6eb7e7b82e..279107726a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -286,8 +286,12 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dependsOnBean : dependsOn) { - getBean(dependsOnBean); + if (isDependent(beanName, dependsOnBean)) { + throw new BeanCreationException("Circular depends-on relationship between '" + + beanName + "' and '" + dependsOnBean + "'"); + } registerDependentBean(dependsOnBean, beanName); + getBean(dependsOnBean); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index eedeff37ff..e0dd5796b6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -412,6 +412,28 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements } } + /** + * Determine whether the specified dependent bean has been registered as + * dependent on the given bean or on any of its transitive dependencies. + * @param beanName the name of the bean to check + * @param dependentBeanName the name of the dependent bean + */ + protected boolean isDependent(String beanName, String dependentBeanName) { + Set dependentBeans = this.dependentBeanMap.get(beanName); + if (dependentBeans == null) { + return false; + } + if (dependentBeans.contains(dependentBeanName)) { + return true; + } + for (String transitiveDependency : dependentBeans) { + if (isDependent(transitiveDependency, dependentBeanName)) { + return true; + } + } + return false; + } + /** * Determine whether a dependent bean has been registered for the given name. * @param beanName the name of the bean to check 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 8360328789..484445cd90 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 @@ -1219,8 +1219,8 @@ public class DefaultListableBeanFactoryTests { } catch (UnsatisfiedDependencyException ex) { // expected - assertTrue(ex.getMessage().indexOf("rod") != -1); - assertTrue(ex.getMessage().indexOf("rod2") != -1); + assertTrue(ex.getMessage().contains("rod")); + assertTrue(ex.getMessage().contains("rod2")); } } @@ -1290,6 +1290,51 @@ public class DefaultListableBeanFactoryTests { assertNull(bean.getSpouse()); } + @Test + public void testDependsOnCycle() { + DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); + RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); + bd1.setDependsOn(new String[] {"tb2"}); + lbf.registerBeanDefinition("tb1", bd1); + RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class); + bd2.setDependsOn(new String[] {"tb1"}); + lbf.registerBeanDefinition("tb2", bd2); + try { + lbf.preInstantiateSingletons(); + fail("Should have thrown BeanCreationException"); + } + catch (BeanCreationException ex) { + // expected + assertTrue(ex.getMessage().contains("Circular")); + assertTrue(ex.getMessage().contains("'tb2'")); + assertTrue(ex.getMessage().contains("'tb1'")); + } + } + + @Test + public void testImplicitDependsOnCycle() { + DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); + RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); + bd1.setDependsOn(new String[] {"tb2"}); + lbf.registerBeanDefinition("tb1", bd1); + RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class); + bd2.setDependsOn(new String[] {"tb3"}); + lbf.registerBeanDefinition("tb2", bd2); + RootBeanDefinition bd3 = new RootBeanDefinition(TestBean.class); + bd3.setDependsOn(new String[] {"tb1"}); + lbf.registerBeanDefinition("tb3", bd3); + try { + lbf.preInstantiateSingletons(); + fail("Should have thrown BeanCreationException"); + } + catch (BeanCreationException ex) { + // expected + assertTrue(ex.getMessage().contains("Circular")); + assertTrue(ex.getMessage().contains("'tb3'")); + assertTrue(ex.getMessage().contains("'tb1'")); + } + } + @Test(expected=NoSuchBeanDefinitionException.class) public void testGetBeanByTypeWithNoneFound() { DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); @@ -2174,7 +2219,7 @@ public class DefaultListableBeanFactoryTests { AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); beanDefinition.setScope("he put himself so low could hardly look me in the face"); - final DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); + DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); factory.registerBeanDefinition("testBean", beanDefinition); factory.getBean("testBean"); } @@ -2186,8 +2231,7 @@ public class DefaultListableBeanFactoryTests { RootBeanDefinition parent = new RootBeanDefinition(); parent.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); - AbstractBeanDefinition child = BeanDefinitionBuilder - .childBeanDefinition("parent").getBeanDefinition(); + AbstractBeanDefinition child = BeanDefinitionBuilder.childBeanDefinition("parent").getBeanDefinition(); child.setBeanClass(TestBean.class); child.setScope(theChildScope);