An overview of the

This article describes the process and implementation principles of Spring circular dependencies. Spring version: 5.1.14

Spring loop dependencies

In the following article, I will describe the process and implementation of circular dependency in detail, with the Spring source process as the leading step analysis. It contains some pre-knowledge on attribute assignment, which you can find in previous articles.

Spring addresses those circular dependency scenarios

Spring only supports cyclic dependencies for singleton non-lazy load scenarios, and does not address construct injection and lazy load cyclic dependencies.

Spring circular dependency definitions

As the name suggests, circular dependency means that there are classes A and B that refer to B in A and refer to A in B. This is a simple circular dependency. For example, inject B through constructors in class A and inject B through constructors in class B. This way each other into the Spring IOC container is unable to complete, back to throw BeanCurrentlyInCreationException. During dependency injection, the cyclic dependency between Bean A and Bean B requires that one Bean be injected by the other Bean before the initialization is complete. It’s a chicken-and-egg kind of problem. A simple example:

// A depends on B
@Component
public class A {

	@Autowired
	private B b;
}

// B depends on A
@Component
public class B {

	@Autowired
	private A a;
}
Copy the code

Spring cycles dependency processing

Here is A sketch I made myself. The precondition is: Class A and class B are cyclically dependent on each other. When A loads first and B loads later, it should be fromdoCreateBeanStart a step-by-step search.Take the circular dependency process for classes A and B above

  1. First entry methodAbstractAutowireCapableBeanFactory#doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)With this as the entrance through the following several processes
//1. Create a Bean instance to obtain a BeanWrapper object
instanceWrapper = createBeanInstance(beanName, mbd, args);
/ / 2. Merger BeanDefinitionPostProcessor
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
//3. Attribute padding
populateBean(beanName, mbd, instanceWrapper);
//4. Bean initialization
exposedObject = initializeBean(beanName, exposedObject, mbd);
//5. Register the Bean destruction method
registerDisposableBeanIfNecessary(beanName, bean, mbd);
Copy the code
  1. Several steps of cyclic dependency, (1). Fill in the propertypopulateBeanMethod is called before theinstanceWrapperPut it in level three cachesingletonFactories, the calling code:addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));Notice that the level 3 cache here holds theObjectFactoryObject factory, in order to solve the problem that the object might be proxied, if we cache the object directly then there will be Bean objectsaThe AOP proxy details will be covered in a future article. The first ()->{} usage is a standard Ramda expression. Let’s seeaddSingletonFactoryHow the method is implemented.
protected void addSingletonFactory(String beanName, ObjectFactory
        singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            // level 3 cache, which caches the ObjectFactory, which represents the ObjectFactory used to create an object.
            this.singletonFactories.put(beanName, singletonFactory);
            // Level 2 cache, which caches the earlier bean objects. Put the Bean into earlySingletonObjects before the Bean's lifecycle is complete.
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName); }}}Copy the code

(2). Class A looks for A class that depends on class B, and then creates A class B using the getBean method. Class B depends on class A, and then looks for class A using the getBean method. And so here we are. AbstractBeanFactory#doGetBean () ¶ AbstractBeanFactory#doGetBean ();

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// Obtain whether the singleton pool exists
	Object singletonObject = this.singletonObjects.get(beanName);
	// Creating
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			EarlySingletonObjects, a level 2 cache, may hold proxy objects that have been AOP enhanced
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				// The third-level cache singletonFactories are used for early exposure of beans to resolve circular dependenciesObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
				if(singletonFactory ! =null) {
					singletonObject = singletonFactory.getObject();
					// Put it in the level 2 cache
					this.earlySingletonObjects.put(beanName, singletonObject);
					// Remove from level 3 cache
					this.singletonFactories.remove(beanName); }}}}return singletonObject;
}
Copy the code

Since we’re already creating class A, we should have an ObjectFactory for class A and in this case, Bean A uses the level-3 cache singletonFactories to access the level-2 cache earlySingletonObjects. If the factories are accessed multiple times, then we can get them directly in the level-2 cache earlySingletonObjects.

AddSingleton (beanName, singletonObject) (beanName, singletonObject) (beanName, singletonObject); Since A is created first and A depends on B, b needs to be created before A can be created. Let’s go back to the addSingleton method, which basically removes the bean from the level 2 and level 3 caches and places it in the Level 1 cache singletonObjects.

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName); }}Copy the code

Spring circular dependency summary

  1. Three levels of cacheregisteredSingletonsIt seems to me that the role is mainly used as an exposure to an early object, which can be either a proxy factory or a primitive object, depending on the context.
  2. The second level cacheearlySingletonObjectsIt is exposed as an incomplete object, in which the object does not complete property assignment, but the object inside it can be injected to the consumer as an auto-injected object.
  3. Level 1 cachesingletonObjectsA complete object to hold, indicating that the single instance Bean has been created.

Spring source parsing

  • Spring Startup Process
  • The life cycle of Spring Beans
  • Spring Attribute Injection
  • Spring loop dependencies
  • Spring Aop
  • Spring transactions
  • Spring integration MyBatis
  • Spring FAQ