Browse Source

Detect depends-on cycles and throw proper exception

Issue: SPR-7966
pull/423/head
Juergen Hoeller 11 years ago
parent
commit
bd8469990d
  1. 6
      spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
  2. 22
      spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java
  3. 54
      spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java

6
spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java

@ -286,8 +286,12 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp @@ -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);
}
}

22
spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

@ -412,6 +412,28 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements @@ -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<String> 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

54
spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java

@ -1219,8 +1219,8 @@ public class DefaultListableBeanFactoryTests { @@ -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 { @@ -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 { @@ -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 { @@ -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);

Loading…
Cancel
Save