循环依赖
A依赖B,B又依赖A,导致循环嵌套。
Spring中一共又三种循环依赖的现象。
1.构造器中的循环依赖。直接报错
2.单例scope下setter方法的循环依赖。使用三级缓存解决
3.非单例scope的循环依赖。不解决,由用户处理。
spring具体是如何处理的?
1.构造器中的循环依赖。
spring中存在一个正在创建对象的池子,创建对象时添加到该池子,this .singletonsCurrentlylnCreation.add(beanName)。创建完成从池子删除。试图创建某对象时,这样当创建前池中如果已经包含该对象名,直接返回BeanCurrentlylnCreationException 异常表示循环依赖。
2.单例scope时的setter方法循环依赖。
首先spring单例对象的初始化大略分为三步:
createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
populateBean:填充属性,这一步主要是多bean的依赖属性进行填充
initializeBean:调用spring xml中的init 方法。
spring 对单例scope下引入了三级缓存。如下
/** Cache of singleton objects: bean name –> bean instance */
//一级缓存,存放初始化完成的对象
private final Map singletonObjects = new ConcurrentHashMap(256);
//二级缓存,存放已经实例化但未完成初始化的对象
/** Cache of early singleton objects: bean name –> bean instance */
private final Map earlySingletonObjects = new HashMap(16);
//三级缓存,存放进入实例化阶段的对象
/** Cache of singleton factories: bean name –> ObjectFactory */
private final Map> singletonFactories = new HashMap>(16);
getSingleton源码
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
//isSingletonCurrentlyInCreation()判断当前单例bean是否正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//到这步说明一级缓存中不存在,且对象正在创建中,准备从二级缓存获取
//加锁,防止多个线程同时进入,导致某线程从二级三级缓存中获取都为null 引发错误
synchronized (this.singletonObjects) {
//从二级缓存获取
singletonObject = this.earlySingletonObjects.get(beanName);
//从二级获取不到,且允许从三级缓存获取
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//从三级缓存中获取到对象
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//从singletonFactories中移除,并放入earlySingletonObjects中。
//其实也就是从三级缓存移动到了二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
总体流程:
1.先从一级缓存中获取
2.获取不到且对象正在创建,则加锁从二级缓存获取(注意加锁,因为二级和三级缓存都是hashmap 非线程安全)。
3.如果从二级缓存中获取不到,且允许从三级缓存中获取。
4.从三级缓存中获取正在实例化的对象,并转移至二级缓存。
5.返回该对象
三级缓存中的对象,是在createBeanInstance实例化完成后,populateBean填充属性前放入的。
这也说明了为什么构造器中的循环依赖无法解决,因为三级缓存要在实例化完成后才放入呀。
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);
}
}
}
那么循环依赖的事情就清楚了:
实例化A后,将A放入三级缓存singletonFactory,填充属性时发现引用了对象B,则实例化B,将B也放入三级缓存,填充属性是发现依赖A,通过一级缓存二级缓存都没有找到A,最终通过三级缓存获取到A,B完成初始化。A完成属性填充及初始化,放入一级缓存。
从而避免了循环依赖的问题。
用来两级缓存可以吗?
一级缓存:存储单例bean,即 Map<String, Object> singletonObjects( bean name to
bean instance. )
二级缓存:存储提前暴露的bean,真正的解决循环依赖是靠二级缓存的。Map<String, Object>earlySingletonObjects (Cache of early singleton objects: bean name to bean instance.)
三级缓存:存储bean和其要加强的aop处理,如果需要aop增强的bean遇到了循环依赖,则使用该缓存中的aop处理代理增强bean(Cache of singleton factories: bean name to ObjectFactory.)实际上不用三级缓存也可以解决循环依赖,但是如果有需要aop增强的bean时,就要在初始化bean的时候对bean做增强了,这违背了Spring在结合AOP跟Bean的生命周期的设计!
Spring结合AOP跟Bean的生命周期本身就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。
最后一种prototype 作用域下的循环依赖
对于prototype作用域的下的bean,spring的原则是只负责简单的创建,其他的事情交由使用者负责,所以循环依赖只有考用户自己解决了。