• Spring源码解析(五) -- 解决循环依赖


    在上一节Spring源码解析(四)中,有一些代码并没有仔细分析。而这些代码都和spring解决循环依赖相关的。比较重要的有两处

    第一处在 AbstractAutowireCapableBeanFactory的 doCreateBean方法 

    if (instanceWrapper == null) {
                instanceWrapper = createBeanInstance(beanName, mbd, args);
            }
            final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
            Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
            mbd.resolvedTargetType = beanType;
    
            // Allow post-processors to modify the merged bean definition.
            synchronized (mbd.postProcessingLock) {
                if (!mbd.postProcessed) {
                    try {
                        applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                    }
                    catch (Throwable ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Post-processing of merged bean definition failed", ex);
                    }
                    mbd.postProcessed = true;
                }
            }
    
            // 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) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Eagerly caching bean '" + beanName +
                            "' to allow for resolving potential circular references");
                }
                addSingletonFactory(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        return getEarlyBeanReference(beanName, mbd, bean);
                    }
                });
            }
    
            // Initialize the bean instance.
            Object exposedObject = bean;
            try {
                populateBean(beanName, mbd, instanceWrapper);
                if (exposedObject != null) {
                    exposedObject = initializeBean(beanName, exposedObject, mbd);
                }
            }

    注意在  createBeanInstance首先实例化一个对象之后,和对这个对象属性值进行填充之前,spring做了一件事。

    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                    isSingletonCurrentlyInCreation(beanName));
            if (earlySingletonExposure) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Eagerly caching bean '" + beanName +
                            "' to allow for resolving potential circular references");
                }
                addSingletonFactory(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        return getEarlyBeanReference(beanName, mbd, bean);
                    }
                });
            }

    把这个空的对象先暴露到一个缓存中,这里就涉及到一个概念,spring的三级缓存。接下来就通过三级缓存和 循环依赖的概念说明下spring是怎么解决循环依赖的。

      1 解决循环依赖的原理

      Spring循环依赖的理论依据其实是Java基于引用传递,当我们获取到对象的引用时,对象的field或者或属性是可以延后设置的。

      Spring单例对象的初始化其实可以分为三步:

    • createBeanInstance, 实例化,实际上就是调用对应的构造方法构造对象,此时只是调用了构造方法,spring xml中指定的property并没有进行populate
    • populateBean,填充属性,这步对spring xml中指定的property进行populate。@Autowired,@Resource 自动注入都在这里执行的。
    • initializeBean,调用spring xml中指定的init方法,或者AfterPropertiesSet方法。
      会发生循环依赖的步骤集中在第一步和第二步。

      2 三级缓存

      “三级缓存”主要是指  DefaultSingletonBeanRegistry 中的三个缓存

      一级 singletonObjects, 二级 earlySingletonObjects ,三级 singletonFactories

    /** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

      singletonObjects指单例对象的cache,singletonFactories指单例对象工厂的cache,earlySingletonObjects指提前曝光的单例对象的cache。

      3 解决过程源码

      在Spring源码解析(四) 中,分析过了Bean创建的过程,首先Spring会尝试从缓存中获取,这个缓存就是指singletonObjects,主要调用的方法是:

    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 != NULL_OBJECT ? singletonObject : null);}

      首先注意两个对象 

      1  isSingletonCurrentlyInCreation 判断是否当前bean正在创建

      2  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); ObjectFactory又可以成为三级缓存

      判断是否正在创建的逻辑也是在 DefaultSingletonBeanRegistry这个类里,可以说这个类包办了解决循环依赖的全部功能。

    public boolean isSingletonCurrentlyInCreation(String beanName) {
            return this.singletonsCurrentlyInCreation.contains(beanName);
        }

    /** Names of beans that are currently in creation */
    private final Set<String> singletonsCurrentlyInCreation =
    Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

      我们再看看这两个变量是什么时候被赋值的。

      其实这两个变量是在同一个方法里被赋值的,在AbstractBeanFactory的doGetBean方法中,如果一个bean是第一次被创建,那么就会执行到

    if (mbd.isSingleton()) {
                        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                            @Override
                            public Object getObject() throws BeansException {
                                try {
                                    return createBean(beanName, mbd, args);
                                }
                                catch (BeansException ex) {
                                    // Explicitly remove instance from singleton cache: It might have been put there
                                    // eagerly by the creation process, to allow for circular reference resolution.
                                    // Also remove any beans that received a temporary reference to the bean.
                                    destroySingleton(beanName);
                                    throw ex;
                                }
                            }
                        });
                        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                    }

      我们继续看 

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
            Assert.notNull(beanName, "'beanName' must not be null");
            synchronized (this.singletonObjects) {
                Object singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    if (this.singletonsCurrentlyInDestruction) {
                        throw new BeanCreationNotAllowedException(beanName,
                                "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                                "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                    }
                    beforeSingletonCreation(beanName);
                    boolean newSingleton = false;
                    boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = new LinkedHashSet<Exception>();
                    }
                    try {
                        singletonObject = singletonFactory.getObject();
                        newSingleton = true;
                    }
                    catch (IllegalStateException ex) {
                        // Has the singleton object implicitly appeared in the meantime ->
                        // if yes, proceed with it since the exception indicates that state.
                        singletonObject = this.singletonObjects.get(beanName);
                        if (singletonObject == null) {
                            throw ex;
                        }
                    }
                    catch (BeanCreationException ex) {
                        if (recordSuppressedExceptions) {
                            for (Exception suppressedException : this.suppressedExceptions) {
                                ex.addRelatedCause(suppressedException);
                            }
                        }
                        throw ex;
                    }
                    finally {
                        if (recordSuppressedExceptions) {
                            this.suppressedExceptions = null;
                        }
                        afterSingletonCreation(beanName);
                    }
                    if (newSingleton) {
                        addSingleton(beanName, singletonObject);//此时该bean所有的步骤都走完了,包括成员变量的填充,所以就没必要再要二级和三级缓存了,只需要保留一级缓存
                    }
                }
                return (singletonObject != NULL_OBJECT ? singletonObject : null);
            }
        }

      beforeSingletonCreation就是会把这个beanName加入到 singletonsCurrentlyInCreation 这个set中,而 afterSingletonCreation 会从singletonsCurrentlyInCreation 中删除这个beanName。

      我们继续看 getSingleton会执行  singletonFactory.getObject();而这个逻辑是在

    new ObjectFactory<Object>() {
                            @Override
                            public Object getObject() throws BeansException {
                                try {
                                    return createBean(beanName, mbd, args);
                                }

      所以继续分析AbstractAutowireCapableBeanFactory的 createBean,在createBean中真正干活的是 doCreateBean。

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
                throws BeanCreationException {
    
            // Instantiate the bean.
            BeanWrapper instanceWrapper = null;
            if (mbd.isSingleton()) {
                instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
            }
            if (instanceWrapper == null) {
                instanceWrapper = createBeanInstance(beanName, mbd, args);
            }
            final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
            Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
            mbd.resolvedTargetType = beanType;
    
            // Allow post-processors to modify the merged bean definition.
            synchronized (mbd.postProcessingLock) {
                if (!mbd.postProcessed) {
                    try {
                        applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                    }
                    catch (Throwable ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Post-processing of merged bean definition failed", ex);
                    }
                    mbd.postProcessed = true;
                }
            }
    
            // 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) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Eagerly caching bean '" + beanName +
                            "' to allow for resolving potential circular references");
                }
                addSingletonFactory(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        return getEarlyBeanReference(beanName, mbd, bean);
                    }
                });
            }

      经过createBeanInstance的调用后,bean已经被创建出来了,只是属性值还未填充,也就是说他还只是一个空对象。

      然后会执行

     addSingletonFactory(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        return getEarlyBeanReference(beanName, mbd, bean);
                    }
                });

      就在这里设置了三级缓存this.singletonFactories.put(beanName, singletonFactory);

      此处就是解决循环依赖的关键,这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来的。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。

      最后再看下三级缓存在哪里被使用的,如果三级缓存里有这个bean那么就不用再创建了,直接拿到这个bean

    @SuppressWarnings("unchecked")
        protected <T> T doGetBean(
                final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
                throws BeansException {
    
            final String beanName = transformedBeanName(name);
            Object bean;
    
            // Eagerly check singleton cache for manually registered singletons.
            Object sharedInstance = getSingleton(beanName);
            if (sharedInstance != null && args == null) {
                if (logger.isDebugEnabled()) {
                    if (isSingletonCurrentlyInCreation(beanName)) {
                        logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                                "' that is not fully initialized yet - a consequence of a circular reference");
                    }
                    else {
                        logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                    }
                }
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
            }

    4 为什么是三级缓存

      为什么采用三级缓存呢,以下是我个人的理解。首先这三个缓存的作用不同。

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
    
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
    
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

    singletonFactories 的value部分存的是ObjectFactory,只有在调用getSingletion时,会执行ObjectFactory的getObject方法,并存入earlySingletonObjects。
    注意一下 singletonObjects 和 earlySingletonObjects初始大小的不同。

    earlySingletonObjects在一个bean完全完成了初始化后就会清理,所以没必要有太大的容量。

    而 singletonObjects经过我的观察后,只有在spring容器 AbstractApplicationContext 调用 close后才会进行清理,也就是说在spring容器的生命周期里他是一直存在的。
    因此总结下,singletonFactories只会被执行一次,执行过后 实例化的bean 存入 earlySingletonObjects,并清理singletonFactories。

    earlySingletonObjects在创建bean过程中,getSingleton中会被调用,如果一个bean已经存在在 earlySingletonObjects,则不会继续走创建bean流程。并且在bean初始化完成后被清理。
    生命周期比ObjectFactory能长一点。
    singletonObjects 生命周期是最长的,因此它才是一级缓存。

    earlySingletonObjects经过观察它的生命周期是一个bean第一次被调用getSinfleton之后,三级缓存就会清除,放入二级缓存,这是它的开始。它的结束是该bean完全初始化完毕。

    所以,一种可能的场景就是A 包括了B,C,B和C都依赖A。A初始化的时候,会将自己先暴露到三级缓存。这样B在初始化自己的时候,发现要依赖A,此时第一次调用三级缓存,同时A从三级缓存

    就到了二级缓存。A又发现自己需要依赖C,就初始化C,然后C发现自己依赖A,此时就会调用getSingleton,这个是就是从二级缓存拿的。如果A所有属性都填充好了,就会销毁二级缓存,只保留一级缓存。

    5 为什么构造方法的循环依赖无法解决

      有之前的只是铺垫,这个问题理解起来就容易多了。ClassA初始化时,在实例化之前会把bean name放到Set<String> singletonsCurrentlyInCreation 。然后当执行ClassA的构造函数时,发现要依赖ClassB。在实例化ClassB时,又需要实例化ClassA,结果发现A的bean并不在缓存里,但是还在singletonsCurrentlyInCreation 里。这就发生了bean同时创建的异常。

  • 相关阅读:
    [dev][ipsec][esp] ipsec链路中断的感知问题
    [dev] Go语言查看doc与生成API doc
    [daily]gtk程序不跟随系统的dark主题
    [dev] Go的协程切换问题
    基因程序设计/基因编程/遗传编程
    [daily][emacs][go] 配置emacs go-mode的编辑环境以及环境变量问题
    Java Spring中@Query中使用JPQL LIKE 写法
    JavaScript 使用HTML DOM的oninput事件,实时监听value值变化
    Java中执行.exe文件
    Java关于List<String> 进行排序,重写Comparator()方法
  • 原文地址:https://www.cnblogs.com/juniorMa/p/13785583.html
Copyright © 2020-2023  润新知