基于Spring-5.1.5.RELEASE

问题

都晓得Spring通过三级缓存来解决循环依赖的问题。然而是不是必须三级缓存能力解决,二级缓存不能解决吗?
要剖析是不是能够去掉其中一级缓存,就先过一遍Spring是如何通过三级缓存来解决循环依赖的。

循环依赖

所谓的循环依赖,就是两个或则两个以上的bean相互依赖对方,最终造成闭环。比方“A对象依赖B对象,而B对象也依赖A对象”,或者“A对象依赖B对象,B对象依赖C对象,C对象依赖A对象”;相似以下代码:

public class A {    private B b;}public class B {    private A a;}

惯例状况下,会呈现以下状况:

  1. 通过构建函数创立A对象(A对象是半成品,还没注入属性和调用init办法)。
  2. A对象须要注入B对象,发现对象池(缓存)里还没有B对象(对象在创立并且注入属性和初始化实现之后,会放入对象缓存里)。
  3. 通过构建函数创立B对象(B对象是半成品,还没注入属性和调用init办法)。
  4. B对象须要注入A对象,发现对象池里还没有A对象。
  5. 创立A对象,循环以上步骤。

三级缓存

Spring解决循环依赖的核心思想在于提前曝光

  1. 通过构建函数创立A对象(A对象是半成品,还没注入属性和调用init办法)。
  2. A对象须要注入B对象,发现缓存里还没有B对象,将半成品对象A放入半成品缓存
  3. 通过构建函数创立B对象(B对象是半成品,还没注入属性和调用init办法)。
  4. B对象须要注入A对象,从半成品缓存里取到半成品对象A
  5. B对象持续注入其余属性和初始化,之后将实现品B对象放入实现品缓存
  6. A对象持续注入属性,从实现品缓存中取到实现品B对象并注入。
  7. A对象持续注入其余属性和初始化,之后将实现品A对象放入实现品缓存

其中缓存有三级:

/** Cache of singleton objects: bean name to bean instance. */private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/** Cache of early singleton objects: bean name to bean instance. */private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);/** Cache of singleton factories: bean name to ObjectFactory. */private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
缓存阐明
singletonObjects第一级缓存,寄存可用的成品Bean
earlySingletonObjects第二级缓存,寄存半成品的Bean半成品的Bean是已创建对象,然而未注入属性和初始化。用以解决循环依赖。
singletonFactories第三级缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。用以解决循环依赖。

要理解原理,最好的办法就是浏览源码,从创立Bean的办法AbstractAutowireCapableBeanFactor.doCreateBean动手。

1. 在结构Bean对象之后,将对象提前曝光到缓存中,这时候曝光的对象仅仅是结构实现,还没注入属性初始化

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory        implements AutowireCapableBeanFactory {    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)            throws BeanCreationException {                    ……        // 是否提前曝光        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&                isSingletonCurrentlyInCreation(beanName));        if (earlySingletonExposure) {            if (logger.isTraceEnabled()) {                logger.trace("Eagerly caching bean '" + beanName +                        "' to allow for resolving potential circular references");            }            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));        }        ……    }   }     

2. 提前曝光的对象被放入Map<String, ObjectFactory<?>> singletonFactories缓存中,这里并不是间接将Bean放入缓存,而是包装成ObjectFactory对象再放入。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {    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);            }        }    }}public interface ObjectFactory<T> {    T getObject() throws BeansException;}    

3. 为什么要包装一层ObjectFactory对象?

如果创立的Bean有对应的代理,那其余对象注入时,注入的应该是对应的代理对象;然而Spring无奈提前晓得这个对象是不是有循环依赖的状况,而失常状况下(没有循环依赖状况),Spring都是在创立好实现品Bean之后才创立对应的代理。这时候Spring有两个抉择:

  1. 不论有没有循环依赖,都提前创立好代理对象,并将代理对象放入缓存,呈现循环依赖时,其余对象间接就能够取到代理对象并注入。
  2. 不提前创立好代理对象,在呈现循环依赖被其余对象注入时,才实时生成代理对象。这样在没有循环依赖的状况下,Bean就能够按着Spring设计准则的步骤来创立。

Spring抉择了第二种形式,那怎么做到提前曝光对象而又不生成代理呢?
Spring就是在对象外面包一层ObjectFactory,提前曝光的是ObjectFactory对象,在被注入时才在ObjectFactory.getObject形式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map<String, Object> earlySingletonObjects
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory        implements AutowireCapableBeanFactory {    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {        Object exposedObject = bean;        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {            for (BeanPostProcessor bp : getBeanPostProcessors()) {                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);                }            }        }        return exposedObject;    }}

为了避免对象在前面的初始化(init)时反复代理,在创立代理时,earlyProxyReferences缓存会记录已代理的对象。

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {    private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);                @Override    public Object getEarlyBeanReference(Object bean, String beanName) {        Object cacheKey = getCacheKey(bean.getClass(), beanName);        this.earlyProxyReferences.put(cacheKey, bean);        return wrapIfNecessary(bean, beanName, cacheKey);    }        }        

4. 注入属性和初始化

提前曝光之后:

  1. 通过populateBean办法注入属性,在注入其余Bean对象时,会先去缓存里取,如果缓存没有,就创立该对象并注入。
  2. 通过initializeBean办法初始化对象,蕴含创立代理。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory        implements AutowireCapableBeanFactory {    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)            throws BeanCreationException {        ……        // Initialize the bean instance.        Object exposedObject = bean;        try {            populateBean(beanName, mbd, instanceWrapper);            exposedObject = initializeBean(beanName, exposedObject, mbd);        }        catch (Throwable ex) {            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {                throw (BeanCreationException) ex;            }            else {                throw new BeanCreationException(                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);            }        }        ……    }        }    // 获取要注入的对象public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {    protected Object getSingleton(String beanName, boolean allowEarlyReference) {        // 一级缓存        Object singletonObject = this.singletonObjects.get(beanName);        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {            synchronized (this.singletonObjects) {                // 二级缓存                singletonObject = this.earlySingletonObjects.get(beanName);                if (singletonObject == null && allowEarlyReference) {                    // 三级缓存                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);                    if (singletonFactory != null) {                        singletonObject = singletonFactory.getObject();                        this.earlySingletonObjects.put(beanName, singletonObject);                        this.singletonFactories.remove(beanName);                    }                }            }        }        return singletonObject;    }}    

5. 放入已实现创立的单例缓存

在经验了以下步骤之后,最终通过addSingleton办法将最终生成的可用的Bean放入到单例缓存里。

  1. AbstractBeanFactory.doGetBean ->
  2. DefaultSingletonBeanRegistry.getSingleton ->
  3. AbstractAutowireCapableBeanFactory.createBean ->
  4. AbstractAutowireCapableBeanFactory.doCreateBean ->
  5. DefaultSingletonBeanRegistry.addSingleton
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {    /** Cache of singleton objects: bean name to bean instance. */    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);    /** Cache of singleton factories: bean name to ObjectFactory. */    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);    /** Cache of early singleton objects: bean name to bean instance. */    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);    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);        }    }}    

二级缓存

下面第三步《为什么要包装一层ObjectFactory对象?》里讲到有两种抉择:

  1. 不论有没有循环依赖,都提前创立好代理对象,并将代理对象放入缓存,呈现循环依赖时,其余对象间接就能够取到代理对象并注入。
  2. 不提前创立好代理对象,在呈现循环依赖被其余对象注入时,才实时生成代理对象。这样在没有循环依赖的状况下,Bean就能够按着Spring设计准则的步骤来创立。

Sping抉择了第二种,如果是第一种,就会有以下不同的解决逻辑:

  1. 提前曝光半成品时,间接执行getEarlyBeanReference创立到代理,并放入到缓存earlySingletonObjects中。
  2. 有了上一步,那就不须要通过ObjectFactory提早执行getEarlyBeanReference,也就不须要singletonFactories这一级缓存。

这种解决形式可行吗?
这里做个试验,对AbstractAutowireCapableBeanFactory做个小革新,在放入三级缓存之后立即取出并放入二级缓存,这样三级缓存的作用就齐全被疏忽掉,就相当于只有二级缓存

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory        implements AutowireCapableBeanFactory {    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)            throws BeanCreationException {                    ……        // 是否提前曝光        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&                isSingletonCurrentlyInCreation(beanName));        if (earlySingletonExposure) {            if (logger.isTraceEnabled()) {                logger.trace("Eagerly caching bean '" + beanName +                        "' to allow for resolving potential circular references");            }            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));            // 立即从三级缓存取出放入二级缓存            getSingleton(beanName, true);        }        ……    }   }     

测试后果是能够的,并且从源码上剖析能够得出两种形式性能是一样的,并不会影响到Sping启动速度。那为什么Sping不抉择二级缓存形式,而是要额定加一层缓存?
如果要应用二级缓存解决循环依赖,意味着Bean在结构完后就创立代理对象,这样违反了Spring设计准则。Spring联合AOP跟Bean的生命周期,是在Bean创立齐全之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来实现的,在这个后置解决的postProcessAfterInitialization办法中对初始化后的Bean实现AOP代理。如果呈现了循环依赖,那没有方法,只有给Bean先创立代理,然而没有呈现循环依赖的状况下,设计之初就是让Bean在生命周期的最初一步实现代理而不是在实例化后就立马实现代理。

参考:

《面试官:聊聊Spring源码的生命周期、循环依赖》
《面试必杀技,讲一讲Spring中的循环依赖》