Spring循环依赖

什么是循环依赖

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

 

这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,最终会导致内存溢出错误,除非有终结条件。

Spring中循环依赖场景有:
(1)构造器的循环依赖
(2)field属性的循环依赖。

Spring怎么解决循环依赖

Spring的循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的field或则属性是可以延后设置的(但是构造器必须是在获取引用之前)。

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

(1)createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象

(2)populateBean:填充属性,这一步主要是多bean的依赖属性进行填充

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

从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二部。也就是构造器循环依赖和field循环依赖。

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

三级缓存是哪三级?

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

 1 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
 2      /** Cache of singleton objects: bean name --> bean instance */ 
 3      //单例bean的cache 一级缓存 存的是Bean(Bean生命周期完成之后,被spring容器管理的对象)
 4     private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
 5      /** Cache of singleton factories: bean name --> ObjectFactory */
 6      //单例对象工厂的cache 二级缓存 存的是工厂 (循环依赖存放,用于Aop,在工厂中对象被创建时可以做其他的事情)
 7     private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
 8      /** Cache of early singleton objects: bean name --> bean instance */ 
 9       //提前暴光的单例对象的Cache 三级缓存 存的是对象(new Object()) (二级缓存工厂创建一个对象之后,存入三级缓存)
10     private final Map<String, Object> earlySingletonObjects = new HashMap(16);
11   }

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

 //DefaultSingletonBeanRegistry
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从 单例对象中获取bean Object singletonObject
= this.singletonObjects.get(beanName);
// 获取到对象为空 并且当前对象正在创建中(new 对象(完成))
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { Map var4 = this.singletonObjects; synchronized(this.singletonObjects) {
//从earlySingletonObjects中获取 三级缓存 singletonObject
= this.earlySingletonObjects.get(beanName);
//获取不到且允许singletonFactories通过getObject()获取,就从二级缓存singletonFactory.getObject()(二级缓存)获取
if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject();
//从二级缓存获取,放入到三级缓存,并从二级缓存中移除
this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }

上面的代码需要解释两个方法:

isSingletonCurrentlyInCreation()

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

allowEarlyReference

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

整个过程如下:

 获取到了则:

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

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

如何解决循环依赖的?

从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于singletonFactories这个二级cache。这个cache的类型是ObjectFactory,定义如下:

public interface ObjectFactory<T> {
T getObject() throws BeansException;
}

这个接口在下面被引用

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        Map var3 = this.singletonObjects;
        synchronized(this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }

        }
    }

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

如A引用B ,B引用A: 

  1. A对象初始化,提前暴露对象ObjectFactory到 "当前正在创建的bean池->singletonFactories" 三级缓存
  2. A对象开始第二步设置属性,发现自己依赖对象B,尝试singletonObjects.get(B),没有获取到对象B,创建对象B,ObjectFactory并放入到“当前正在创建的bean池” 三级缓存
  3. 对象B开始设置自己的属性,发现依赖了A对象,开始singletonObjects.get(A),没有获取到,因为A对象才创建了第一步,二和三还没有走,尝试从earlySingletonObjects(二级缓存),没有获取到,尝试从三级缓存获取,得到对象(虽然A没有初始化完成,但是能得到对象:对象引用能定位到堆中的对象)(并从三级对象移除,放入到二级对象中此时A在二级缓存中),此时B对象初始化完成,将自己存入一级缓存中
  4. A此时从earlySingletonObjects(二级缓存)得到对象B,完成自己的二,三步 
 

为什么A的构造函数中不能依赖对象B?

因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。
  

为什么要将二级缓存remove并且放入到三级缓存?

   protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            Map var4 = this.singletonObjects;
            synchronized(this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                          //问题1为什么要从二级缓存删除 放入三级缓存?
                        this.earlySingletonObjects.put(beanName, singletonObject);
                         //问题2 为什么要删除?
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }

        return singletonObject;
    }
问题1:解决性能问题:二级缓存的value是一个创建Bean的工厂,工厂创建bean肯定是耗时的,如果当前的Bean被很多地方循环依赖了,那么无需多次通过工厂创建对象,如果当前对象已经
被工厂创建过了,就从二级缓存移除放入三级缓存,下次再有该bean的获取,就可以直接从三级缓存里面获取了,符合源码中的从三级缓存获取(
  singletonObject = this.earlySingletonObjects.get(beanName);

问题2:其实三级缓存的最终目的的都是保存同一个对象,所以三个缓存里面不可能都存放一样的对象,既然已经存放到三级缓存了,二级缓存也没有必要了,从map里面remove是为了GC。

一级缓存(对象Bean)的存放
    protected void addSingleton(String beanName, Object singletonObject) {
        Map var3 = this.singletonObjects;
        synchronized(this.singletonObjects) {
             //存放一级缓存
            this.singletonObjects.put(beanName, singletonObject);
            //把 二级,三级缓存 当前的对象是删除
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

二级缓存(工厂)的存放

  protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        Map var3 = this.singletonObjects;
        synchronized(this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }

        }
    }

原文地址:https://www.cnblogs.com/fanBlog/p/12457704.html