spring如何解决循环依赖

前言
原文发布于: http://blog.ztgreat.cn/article/59

前面分析了Spring BeanFactory,接着分析了Spring IOC的初始化过程,对整个流程有了一定的认识,当然没有面面俱到,当然也不可能,我自己本身定位就是把握主要脉络,前面遗留了一个问题,就是在Spring IOC最后初始化单例bean的时候,针对循环依赖的处理问题,学习一下思想,处理方式,这是这篇文章的主要目的。

循环依赖?
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。

Spring中循环依赖场景有:

(1)构造器的循环依赖 (没法解决)

(2)field属性的循环依赖。

是否存在循环依赖
我们知道Spring 在创建Bean的时候如果需要创建依赖Bean,则会进行递归调用,那么这样检测循环依赖相对比较容易了,Bean在创建的时候可以给该Bean打个标记,如果递归调用回来发现正在创建中的话,即说明了循环依赖了(是不是有种感觉树的递归遍历)。

Spring怎么解决循环依赖
Spring 在处理属性循环依赖的情况时主要是通过延迟设置来解决的,当bean被实例化后,此时还没有进行依赖注入,当进行依赖注入的时候,发现依赖的bean已经处于创建中了,那么通过可以设置依赖,虽然依赖的bean没有初始化完成,但是这个可以后面再进行设置,至少得先建立bean之间的引用。

Spring的单例对象的初始化主要分为三步:

(1)createBeanInstance:实例化

(2)populateBean:填充属性

(3)initializeBean:调用spring xml中的init 方法。

第一步存在构造器循环依赖

第二步存在属性循环依赖

那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中

Spring为了解决单例的循环依赖问题,使用了三级缓存。

首先我们看源码,三级缓存主要指:

DefaultSingletonBeanRegistry:

  1.  
    /** Cache of singleton objects: bean name --> bean instance */
  2.  
    private final Map singletonObjects = new ConcurrentHashMap(256);
  3.  
     
  4.  
    /** Cache of singleton factories: bean name --> ObjectFactory */
  5.  
    private final Map> singletonFactories = new HashMap>(16);
  6.  
     
  7.  
    /** Cache of early singleton objects: bean name --> bean instance */
  8.  
    private final Map earlySingletonObjects = new HashMap(16);
  9.  
     


这三级缓存分别指:
singletonFactories : 单例对象工厂的cache
earlySingletonObjects :提前暴光的单例对象的Cache
singletonObjects:单例对象的cache

我们在创建bean的时候,首先是从cache中获取这个单例的bean,这个缓存就是singletonObjects。主要调用方法就就是:

  1.  
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  2.  
        //获取bean
  3.  
        Object singletonObject = this.singletonObjects.get(beanName);
  4.  
        
  5.  
        //如果该bean在创建中
  6.  
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  7.  
            synchronized (this.singletonObjects) {
  8.  
                //从提前曝光的集合中获取bean
  9.  
                singletonObject = this.earlySingletonObjects.get(beanName);
  10.  
                
  11.  
                //没找到,允许提前引用
  12.  
                if (singletonObject == null && allowEarlyReference) {
  13.  
                    获取factory
  14.  
                    ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
  15.  
                    if (singletonFactory != null) {
  16.  
                        //通过factory 获取bean
  17.  
                        singletonObject = singletonFactory.getObject();
  18.  
                        
  19.  
                        //提前曝光
  20.  
                        this.earlySingletonObjects.put(beanName, singletonObject);
  21.  
                        this.singletonFactories.remove(beanName);
  22.  
                    }
  23.  
                }
  24.  
            }
  25.  
        }
  26.  
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
  27.  
    }


上面的代码需要解释两个参数:

isSingletonCurrentlyInCreation()判断当前单例bean是否正在创建中,也就是没有初始化完成(比如在A的populateBean过程中依赖了B对象,得先去创建B对象,这时的A就是处于创建中的状态。

allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象

getSingleton()的整个过程,Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取

如果获取到了则:

  1.  
    this.earlySingletonObjects.put(beanName, singletonObject);
  2.  
                            this.singletonFactories.remove(beanName);


从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。

从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于singletonFactories这个三级cache。

在上篇文章中,我们分析了Spring IOC的初始化过程,我们再来回顾一下 AbstractAutowireCapableBeanFactory中的doCreateBean方法:

  1.  
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
  2.  
                 throws BeanCreationException {
  3.  
     
  4.  
             // Instantiate the bean.
  5.  
            BeanWrapper instanceWrapper = null;
  6.  
             if (mbd.isSingleton()) {
  7.  
                 //移除BeanWrapper缓存
  8.  
                instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  9.  
            }
  10.  
             if (instanceWrapper == null) {
  11.  
                 //创建 BeanWrapper
  12.  
                instanceWrapper = createBeanInstance(beanName, mbd, args);
  13.  
            }
  14.  
             //获得bean 实例
  15.  
             final Object bean = instanceWrapper.getWrappedInstance();
  16.  
             //获取实例化对象的类型
  17.  
            Class beanType = instanceWrapper.getWrappedClass();
  18.  
             if (beanType != NullBean.class) {
  19.  
                mbd.resolvedTargetType = beanType;
  20.  
            }
  21.  
     
  22.  
             // Allow post-processors to modify the merged bean definition.
  23.  
             //调用PostProcessor后置处理器
  24.  
             synchronized (mbd.postProcessingLock) {
  25.  
                 if (!mbd.postProcessed) {
  26.  
                     try {
  27.  
                        applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
  28.  
                    }
  29.  
                     catch (Throwable ex) {
  30.  
                         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
  31.  
                                 "Post-processing of merged bean definition failed", ex);
  32.  
                    }
  33.  
                    mbd.postProcessed = true;
  34.  
                }
  35.  
            }
  36.  
     
  37.  
             // Eagerly cache singletons to be able to resolve circular references
  38.  
             // even when triggered by lifecycle interfaces like BeanFactoryAware.
  39.  
             // 下面代码是为了解决循环依赖的问题
  40.  
             boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  41.  
                    isSingletonCurrentlyInCreation(beanName));
  42.  
             //提前曝光bean        
  43.  
             if (earlySingletonExposure) {
  44.  
                 //这里是重点
  45.  
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  46.  
            }
  47.  
     
  48.  
             // Initialize the bean instance.
  49.  
            Object exposedObject = bean;
  50.  
             try {
  51.  
                 //实例化后,需要进行依赖注入
  52.  
                populateBean(beanName, mbd, instanceWrapper);
  53.  
                // 这里就是处理 bean 初始化完成后的各种回调,例如init-method 配置,BeanPostProcessor接口
  54.  
                exposedObject = initializeBean(beanName, exposedObject, mbd);
  55.  
            }
  56.  
             catch (Throwable ex) {
  57.  
                 if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
  58.  
                     throw (BeanCreationException) ex;
  59.  
                }
  60.  
                 else {
  61.  
                     throw new BeanCreationException(
  62.  
                            mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
  63.  
                }
  64.  
            }
  65.  
      
  66.  
             if (earlySingletonExposure) {
  67.  
                 //如果已经提交曝光了bean,那么就从缓存中获取bean
  68.  
                Object earlySingletonReference = getSingleton(beanName, false);
  69.  
                 if (earlySingletonReference != null) {
  70.  
                     //根据名称获取的以注册的Bean和正在实例化的Bean是同一个
  71.  
                     if (exposedObject == bean) {
  72.  
                        exposedObject = earlySingletonReference;
  73.  
                    }
  74.  
                     else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
  75.  
                        String[] dependentBeans = getDependentBeans(beanName);
  76.  
                        Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
  77.  
                         for (String dependentBean : dependentBeans) {
  78.  
                             if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
  79.  
                                actualDependentBeans.add(dependentBean);
  80.  
                            }
  81.  
                        }
  82.  
                         if (!actualDependentBeans.isEmpty()) {
  83.  
                             throw new BeanCurrentlyInCreationException(beanName,
  84.  
                                     "Bean with name '" + beanName + "' has been injected into other beans [" +
  85.  
                                    StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
  86.  
                                     "] in its raw version as part of a circular reference, but has eventually been " +
  87.  
                                     "wrapped. This means that said other beans do not use the final version of the " +
  88.  
                                     "bean. This is often the result of over-eager type matching - consider using " +
  89.  
                                     "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
  90.  
                        }
  91.  
                    }
  92.  
                }
  93.  
            }
  94.  
     
  95.  
             // Register bean as disposable.
  96.  
             try {
  97.  
                registerDisposableBeanIfNecessary(beanName, bean, mbd);
  98.  
            }
  99.  
             catch (BeanDefinitionValidationException ex) {
  100.  
                 throw new BeanCreationException(
  101.  
                        mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
  102.  
            }
  103.  
     
  104.  
             return exposedObject;
  105.  
    }


核心代码是addSingletonFactory方法,该方法介于bean实例化和依赖注入(populateBean)中间,这个对象已经被生产出来了,虽然还没有真正初始化,但是可以根据对象引用能定位到堆中的对象,所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。

  1.  
    protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
  2.  
            Assert.notNull(singletonFactory, "Singleton factory must not be null");
  3.  
             synchronized (this.singletonObjects) {
  4.  
                 if (!this.singletonObjects.containsKey(beanName)) {
  5.  
                     this.singletonFactories.put(beanName, singletonFactory);
  6.  
                     this.earlySingletonObjects.remove(beanName);
  7.  
                     this.registeredSingletons.add(beanName);
  8.  
                }
  9.  
            }
  10.  
    }


如果容器允许提前曝光和引用,那么在singletonFactories 中放入该bean的singletonFactory,这样在依赖注入的时候,就能在第三级缓存中找到singletonFactory 了。

经过这样的操作后,面对 "A 依赖了B,同时B依赖了A"这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步(依赖注入),发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,因此B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化,最后皆大欢喜,A,B都成功的初始化了。

现在 知道为什么 Spring不能解决"A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象"这类问题了,因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。

特殊情况说明
前面说到Spring 利用三级缓存解决了Bean属性循环依赖问题,这里面有个细节问题,接着上面的例子来讲:如果在创建B时,如果调用了A的一些方法,那么就存在危险,因为此时A的属性还未完成初始化,这种情况在spring 配置中指定B的init 方法,这样bean属性注入后会调用init方法,下面简单演示一下这种情况,当然很少会这样做,这里只是钻空子罢了。

A对象:

  1.  
    public class CycleA {
  2.  
     
  3.  
    //依赖B
  4.  
    private CycleB cycleB;
  5.  
    //依赖bean
  6.  
    private MyBean bean;
  7.  
     
  8.  
    public CycleB getCycleB() {
  9.  
    return cycleB;
  10.  
    }
  11.  
     
  12.  
    public void setCycleB(CycleB cycleB) {
  13.  
    this.cycleB = cycleB;
  14.  
    }
  15.  
     
  16.  
    public MyBean getBean() {
  17.  
    return bean;
  18.  
    }
  19.  
     
  20.  
    public void setBean(MyBean bean) {
  21.  
    this.bean = bean;
  22.  
    }
  23.  
     
  24.  
    @Override
  25.  
    public String toString() {
  26.  
    return "this is CycleA";
  27.  
    }
  28.  
    }

B对象

  1.  
    public class CycleB {
  2.  
     
  3.  
        //依赖A
  4.  
        private  CycleA cycleA;
  5.  
     
  6.  
        public CycleA getCycleA() {
  7.  
            return cycleA;
  8.  
        }
  9.  
     
  10.  
        public void setCycleA(CycleA cycleA) {
  11.  
            this.cycleA = cycleA;
  12.  
        }
  13.  
        //init方法中,调用A获取bean属性
  14.  
        public  void init(){
  15.  
            System.out.println( "cycleA:"+cycleA.toString());
  16.  
            //cycleA.getBean() 为 null 
  17.  
            System.out.println( "cycleA->bean:"+cycleA.getBean());
  18.  
        }
  19.  
     
  20.  
        @Override
  21.  
        public String toString() {
  22.  
            return "this is CycleB";
  23.  
        }
  24.  
    }

MyBean对象:

  1.  
    public class MyBean {
  2.  
     
  3.  
        public void init(){
  4.  
            System.out.println( "MyBean:this is init method");
  5.  
        }
  6.  
        @Override
  7.  
        public String toString() {
  8.  
            return "this is My Bean";
  9.  
        }
  10.  
    }

Spring xml配置

  1.  
    <bean id="myBean" class="com.study.spring.bean.MyBean" />
  2.  
    <bean id="cycleA" class="com.study.spring.bean.CycleA" scope="singleton">
  3.  
      <property  name="cycleB" ref="cycleB">property>
  4.  
      <property  name="bean" ref="myBean">property>
  5.  
    bean>
  6.  
    <bean id="cycleB" class="com.study.spring.bean.CycleB" scope="singleton" init-method="init">
  7.  
        <property  name="cycleA" ref="cycleA">property>
  8.  
    bean>


结果:

spring如何解决循环依赖_第1张图片

在Bean对象中虽然可以获取A对象,但是A对象的属性还为注入完毕,没有进行一系列初始化,所以是不安全的。

最后Spring 这种处理循环依赖的这种思想值得我们学习借鉴。

 

转载:https://blog.csdn.net/z69183787/article/details/91636878

你可能感兴趣的:(spring如何解决循环依赖)