zoukankan      html  css  js  c++  java
  • Spring循环依赖三级缓存是否可以减少为二级缓存?

    -     前言     -

    提问:

    我们都知道Spring通过三级缓存来解决循环依赖的问题,那么是不是必须是三级缓存?二级缓存不能解决吗?

    要分析是否能够去掉其中一级缓存,我们需要先过一遍Spring是如何通过三级缓存来解决循环依赖的。2021Java面试宝典

    -     循环依赖     -

    所谓的循环依赖,就是两个或者两个以上的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);

    要了解原理,最好的方法就是阅读源码,从创建Bean的方法AbstractAutowireCapableBeanFactor.doCreateBean入手。

    一. 在构造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));
    }
    ……
    }
    }

    二. 提前曝光的对象被放入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;
    }

    三. 为什么要包装一层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);
    }
    }

    四. 注入属性和初始化

    提前曝光之后:

    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;
    }
    }

    五. 放入已完成创建的单例缓存

    在经历了以下步骤之后,最终通过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在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。2021Java面试宝典

  • 相关阅读:
    从TCP三次握手说起——浅析TCP协议中的疑难杂症
    动态绑定是如何实现的?
    C++对象的内存模型
    C/C++关键字
    libevent库介绍--事件和数据缓冲
    libevent编程疑难解答
    大型工程多个目录下的Makefile写法
    C++中的RAII机制
    C++中的智能指针
    二叉树的非递归遍历
  • 原文地址:https://www.cnblogs.com/bainannan/p/14347300.html
Copyright © 2011-2022 走看看