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.

  1. 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
  2. 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
  3. 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
  1. 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
  1. 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
  1. 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