What are cyclic dependencies?
A has B, B has A, cyclic dependence.
Second, Spring solution
Spring simply solves the problem of Bean loop dependencies with scope singleton.
-
The constructor loop relies on constructor
Spring cannot solve, an exception is thrown directly, org. Springframework. Beans. Factory. BeanCurrentlyInCreationException
Requested bean is currently in creation: Is there an unresolvable circular reference?
-
The source code
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw newBeanCurrentlyInCreationException(beanName); }}Copy the code
-
-
Set loop dependency
Spring resolves the Set loop dependency by pre-exposing the ObjectFactory of the Bean being created, and creating B directly from the ObjectFactory of A with getObject().
-
The source code
// Cache pre-exposed beans /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // Cache the ObjectFactory that creates the Singleton Bean /** Cache of singleton factories: bean name to ObjectFactory. */ private finalMap<String, ObjectFactory<? >> singletonFactories =new HashMap<>(16); IsSingleton && (allowCircularReferences == true) && bean in creation // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) { synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { // Cache the ObjectFactory that created the Bean into singletonFactories this.singletonFactories.put(beanName, singletonFactory); // Remove pre-exposed beanName from earlySingletonObjects this.earlySingletonObjects.remove(beanName); // Registered Bean this.registeredSingletons.add(beanName); }}}/** * This method is called when earlySingletonExposure == true (one of the conditions) */ is created when a singleton Bean needs to be exposed in advance protected Object getSingleton(String beanName, boolean allowEarlyReference) { // From registered singletonObjects Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // From earlySingletonObjects when the singleton is currently being created singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // Retrieve the pre-exposed ObjectFactory from singletonFactoriesObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName); if(singletonFactory ! =null) { // If there is a pre-exposed ObjectFactory, fetch the Bean from it singletonObject = singletonFactory.getObject(); // Cache earlySingletonObjects this.earlySingletonObjects.put(beanName, singletonObject); // Clear the previously exposed ObjectFactory this.singletonFactories.remove(beanName); }}}}return singletonObject; } Copy the code
-
-
Prototype loop dependency
And the constructor cycle as throw: org. Springframework. Beans. Factory. BeanCurrentlyInCreationException: Error creating bean with the name ‘XXX’ : Requested bean is currently in creation: Is there an unresolvable circular reference? abnormal
Three, test,
public class TestA {
private TestB testB;
public TestA(a) {}
public TestA(TestB testB) {
this.testB = testB;
}
public void a(a) {
testB.b();
}
// omit the get/set method
}
Copy the code
public class TestB {
private TestC testC;
public TestB(a) {}
public TestB(TestC testC) {
this.testC = testC;
}
public void b(a) {
testC.c();
}
// omit the get/set method
}
Copy the code
public class TestC {
private TestA testA;
public TestC(a) {}
public TestC(TestA testA) {
this.testA = testA;
}
public void c(a) {
testA.a();
}
// omit the get/set method
}
Copy the code
- Constructor loop dependencies
- spring-constructor-cycle-reference.xml
<bean id="testA" class="com.marksman.spring.cycle.TestA">
<constructor-arg index="0" ref="testB"/>
</bean>
<bean id="testB" class="com.marksman.spring.cycle.TestB">
<constructor-arg index="0" ref="testC"/>
</bean>
<bean id="testC" class="com.marksman.spring.cycle.TestC">
<constructor-arg index="0" ref="testA"/>
</bean>
Copy the code
- test
try {
new ClassPathXmlApplicationContext("/META-INF/spring-constructor-cycle-reference.xml");
} catch (Exception e) {
Throwable throwable = e.getCause().getCause().getCause();
throw throwable;
}
/ / throw BeanCurrentlyInCreationException anomalies
Copy the code
- Set loop dependency
- spring-set-cycle-reference.xml
<bean id="testA" class="com.marksman.spring.cycle.TestA">
<property name="testB" ref="testB"/>
</bean>
<bean id="testB" class="com.marksman.spring.cycle.TestB">
<property name="testC" ref="testC"/>
</bean>
<bean id="testC" class="com.marksman.spring.cycle.TestC">
<property name="testA" ref="testA"/>
</bean>
Copy the code
- test
ApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring-set-cycle-reference.xml");
System.out.println(context.getBean("testA"));
System.out.println(context.getBean("testB"));
System.out.println(context.getBean("testC"));
// Prints references to testA, testB, and testC
Copy the code
- Prototype loop dependency
- spring-prototype-cycle-reference.xml
<bean id="testA" class="com.marksman.spring.cycle.TestA" scope="prototype">
<property name="testB" ref="testB"/>
</bean>
<bean id="testB" class="com.marksman.spring.cycle.TestB" scope="prototype">
<property name="testC" ref="testC"/>
</bean>
<bean id="testC" class="com.marksman.spring.cycle.TestC" scope="prototype">
<property name="testA" ref="testA"/>
</bean>
Copy the code
- test
try {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring-prototype-cycle-reference.xml");
System.out.println(context.getBean("testA"));
System.out.println(context.getBean("testB"));
System.out.println(context.getBean("testC"));
} catch (Exception e) {
Throwable throwable = e.getCause().getCause().getCause();
throw throwable;
}
/ / throw BeanCurrentlyInCreationException anomalies
Copy the code
reference
- Deep Parsing of Spring source code