One, foreword

Spring loop dependencies are one of the interview questions. The interviewer can dig deep into the candidate’s knowledge of Spring’s Bean lifecycle. Spring loop dependency is also one of the difficulties of Spring. The logic is quite convoluting and you need to know the life cycle of Spring beans very well.

What are cyclic dependencies?

@Component
public class A {
	@Autowired
	B b;
}
Copy the code
@Component
public class B {
	@Autowired
	A a;
}
Copy the code

How does Spring handle loop dependencies?

3.1 Use text to describe the lifecycle flow of the two beans on which the specific loop depends

A Life cycle 1. class file 2. beanName to fetch objects from the cache. At first, the singleton pool does not exist, and A is not in the collection being created, so it will not fetch objects from the level 2 cache or level 3 cache. Put the object into the collection being created (beforeSingletonCreation(beanName);) 4. Instantiate an object by reflection (createBeanInstance();) Add object factory to level 2 cache Property population. Find B object needed here, get B from the singleton pool -> NULL -> and B is not in the collection being created -> instantiate B and complete B's lifecycle code [tag ⑤]wait forb init... 8. Fetch bean-related code from level 2 cache [flag ⑦] 9. Add to singleton pool related code [flag ⑥] B lifecycle 1.class file 2. Fetch objects from the cache via beanName. At first, the singleton pool does not exist, and A is not in the collection being created, so it does not go to level 2 cache, level 3 cache to get 3. Put the object into the collection being created (beforeSingletonCreation(beanName);) (singletonObject = singletonFactory.getObject();) 4. Instantiate the object with reflection (createBeanInstance();) 5. Add object factories to level 2 cache 6. Get A from the singleton pool ->null-> Now that A is creating the collection -> get the object from the tier 3 cache (if A has A slice, then return the proxy object), put the object into the tier 2 cache, delete the tier 3 cache 7. Initialize 8. Get beans from level 2 cache 9. Add to singleton poolCopy the code

3.2 Why is level 3 caching needed to solve circular dependencies?

Level 3 cache definition and functions

  • Level 1 cache:singletonObjects. Storage has run outSpringlife-cycleBean. Pay attention to,objectandBeanIn the Spring world, it’s two different things. The object instantiated by reflection is not yet completeSpringLife cycle processes (such as post-processor, etc.),BeanThe command is completeSpringThe entire lifecycle process.
  • Level 2 cache:earlySingletonObjects. Store objects that have been instantiated (created by reflection) but are not finishedSpringObjects of the lifecycle process.
  • Level 3 cache:singletonFactories. storageObject factory. The goal is to generate through the object factory when appropriateAOPProxy object.

How to solve

  1. By exposing yourself in advance (by adding yourself to the level 3 cache) and allowing the dependencies to be injected, you can go through the rest of the process without causing an endless loop.
  2. The exposed object factory purpose is to completeAOPThe agent. Does the object factory know how to create an AOP proxy for an object, but not right away, but when the time is rightAOPCreation of proxy objects.
  3. Level 2 cacheOne of the purposes of in is to ensure that the object is only onceAOPThe agent. When the tertiary cache is calledgetObject()The object returned by the method is stored in a level 2 cache so that subsequent dependencies are judged first when calledThe second level cacheIf there is a target object, return if there is.

3.3 Important code snippets

Code ① – getSingleton

// DefaultSingletonBeanRegistry#getSingleton
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Get beans from level 1 cache, i.e. singleton pool
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // Retrieve objects from the level 2 cache
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // Get the object factory from the level 3 cacheObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
                if(singletonFactory ! =null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName); }}}}return singletonObject;
}
Copy the code

Code ② – beforeSingletonCreation

// DefaultSingletonBeanRegistry#beforeSingletonCreation
protected void beforeSingletonCreation(String beanName) {
    / / 1. InCreationCheckExclusions: determine whether the current objects exist [by] annotations excludeFilters = xx in the collection objects
    / / 2. SingletonsCurrentlyInCreation: add the current object is creating a single example Bean in the collection
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw newBeanCurrentlyInCreationException(beanName); }}Copy the code

Code ③ -createBeanInstance

// AbstractAutowireCapableBeanFactory#createBeanInstance
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // If it is autowiring, various candidate constructors are inferredConstructor<? >[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if(ctors ! =null|| mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || ! ObjectUtils.isEmpty(args)) {// Instantiate objects using inferred practice constructs. If constructor injection is used, this is returned directly
        return autowireConstructor(beanName, mbd, ctors, args);
    }
    ctors = mbd.getPreferredConstructors();
    if(ctors ! =null) {
        // Instantiate the object using the inferred candidate constructor
        return autowireConstructor(beanName, mbd, ctors, null);
    }

    // No special handling: simply use no-arg constructor.
    // If no suitable constructor is inferred (or no special constructor is provided), the default constructor is used
    return instantiateBean(beanName, mbd);
}
Copy the code

Code ④ – addSingletonFactory

// DefaultSingletonBeanRegistry#addSingletonFactory
protected void addSingletonFactory(String beanName, ObjectFactory
        singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName); }}}Copy the code

Code ⑤ – populateBean

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    if (hasInstAwareBpps) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        // There will be three post-processors:
        / / 1, ConfigurationClassPostProcessor $ImportAwareBeanPostProcessor
        // 		2、AutowiredAnnotationBeanPostProcessor
        // 		3、CommonAnnotationBeanPostProcessor
        / / AutowiredAnnotationBeanPostProcessor: parsing @ Autowire property injection
        for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
            / / attribute automatic injection mainly using rear AutowiredAnnotationBeanPostProcessor processor
            PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                if (filteredPds == null) {
                    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                }
                pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                    return; } } pvs = pvsToUse; }}}Copy the code

Code ⑥ – initializeBean

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    if(System.getSecurityManager() ! =null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        // 4-1, Implement Aware (BeanNameAware, BeanClassLoaderAware, BeanclassfactoryAware)
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null| |! mbd.isSynthetic()) {4.2 Before initialization
        // △△ Execute Bean afterprocessor (lifeCycle Callback)
        / / rewrite this method [postProcessBeforeInitialization] will be invoked here
        // lifeCycle callback init by annotation
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        Initialize [InitializingBean]
        // lifeCycle callback init by interface.
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw newBeanCreationException( (mbd ! =null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }


    if (mbd == null| |! mbd.isSynthetic()) {4.4. After initialization
        // Complete AOP, generate dynamic proxy; Event release; Listening to the
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}
Copy the code

Code ⑦ – doCreateBean

// AbstractAutowireCapableBeanFactory#doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
if (earlySingletonExposure) {
    // To resolve cyclic dependencies, get beanName from level 2 cache
    Object earlySingletonReference = getSingleton(beanName, false);
    if(earlySingletonReference ! =null) {
        if(exposedObject == bean) { exposedObject = earlySingletonReference; }... }}...Copy the code

Code ⑧ – getSingleton

// DefaultSingletonBeanRegistry#getSingleton
public Object getSingleton(String beanName, ObjectFactory
        singletonFactory) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
        / / put the current object in the collection of [is creating] (singletonsCurrentlyInCreation)
        beforeSingletonCreation(beanName);
        
        / / getObject method will be called AbstractAutowireCapableBeanFactory createBean method
        singletonObject = singletonFactory.getObject();
        
        // Add to the singleton pooladdSingleton(beanName, singletonObject); }}Copy the code

Iv. Dependency between different injection methods and cycles

Dependency injection Whether circular dependencies can be resolved
All are injected using setter methods can
All use constructor injection Can’t
Inject B in A as setter methods, and inject A in B as constructors can
B is injected into A as A constructor, and A is injected into B as A setter Can’t

Note: A is created first.

Summary:

  1. The main object (the first class to be loaded with loop dependencies, A for short) cannot passThe constructorMethod to inject the dependent Bean object (B for short), which is not restricted and can be injected in any way (Setters, @autowire, constructors).
  2. The A object in the loop dependency cannot be usedThe constructorThe reason for injection is inAbstractAutowireCapableBeanFactory#createBeanInstanceIf do type inference method, the inference is a method to construct instance, B will direct call ` ` AbstractAutowireCapableBeanFactory# autowireConstructor, the method is calledAbstractAutowireCapableBeanFactory#doCreateBeanThe object is created, but since the A object does not expose the factory in advance, it is created step by step, but in codeDefaultSingletonBeanRegistry#getSingletonthebeforeSingletonCreationMethod, because A is creating the collection at this time, forcible addition will returnFalse ‘, an exception is thrown. Related code ③

How about solving circular dependencies?

  • Deferred construction
  • usesetterinjection

Four,

  1. SpringThe team suggested we use itConstructor basedTo manage our dependencies, using assertions against mandatory dependencies. It is recommended to usesetterInjection.
  2. How does Spring address loop dependencies?
    1. SpringthroughThree levels of cacheCircular dependencies are resolved. Other level 1 caches are singleton pools, level 2 caches are early exposure objects, and level 3 caches are early exposure object factories. When A and B are cyclic references, after A is instantiated, expose itself early (that is, add level 3 cache), if A is initializedAOPProxy, a factory object that returns the proxied object or itself if not proxied. When A does the property injection, after the previous instantiation steps, it is now the turn of B’s property injection, callgetBean(a)Get the OBJECT A, because the A process is in the process of creating the collection, and the loop dependency is also issued, so you can get the object factory from the level 3 cache (if A is AOP proxied, it will return the proxy object) and put the object into the level 2 cache, ensuring that A only passes through the AOP proxy once. And then B goesSpringLifecycle processes and into a singleton pool. After B is created, B is injected into A, and A is finishedSpringLifecycle processes. At this point, the loop dependency ends.