spring内部有三级缓存:singletonObjects
一级缓存,用于保存实例化、注入、初始化完成的bean实例earlySingletonObjects
二级缓存,用于保存实例化完成的bean实例singletonFactories
三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。
Spring循环依赖的三种情况:
1.构造器循环依赖
表示通过构造器注入构成的循环依赖, 此依赖是无法解决的 ,只能抛出 BeanCurrent ylnCreationException 异常表示循环依赖。
如在创 TestA 类时, 构造器需要 TestB 类,那将去 TestB estB 类时又发现 需要 TestC 类, 又去创建
TestC 终在创建 TestC 时发现又需要 TestA ,从而形成 个环, 没办法创建Spring 容器将每 个正在创建的 bean 标识符放在 个“当前 bean 池”中 be 标识
柏:在创建过程中将一直保持在这个池中,因此如果在创建 bean 过程中发现自己已经在“当前创建bean 池” 里时,将抛出
BeanCurrentlylnCreationExcept ion 异常表示循环依赖;而对于创建完毕的 bean 将从“当前 bean
池”中清除掉
2.setter循环依赖
l. Spring 容器创建单例“testA bean ,首先根据元参构造器创建 bean ,并暴露 一个ObjectFactory用于返回一个提前暴露一个创建中的 ,并将“testA ”标识符放到“当前创建bean 池”, 然后进行 setter 注入“testB
2. Spring 容器创建单例 testB bean ,首先根据无参构造器创建 bean ,并暴露一 ObjectFactory ”用于返回一个提前暴露 个创建中的 bean ,并将“testB ”标识符放到“当前 创建bean 池”,然后进行 setter注入“circle
3. Spring 容器创建单例“testC bean ,首先根据元参构造器创建 bean ,并暴露一 ObjectFactory ”用于返回 个提前暴露一个创建中的 bean ,并将“testC ”标识符放到“当前创建bean 池”,然后进行 setter注入“testA 进行注入“testA ”时由于提前暴露了“ jectFactory 工厂,从而使用它返回提前暴露一个创建中的 bean
4. 最后在依赖注入 testB ”和“testA ”,完成 setter 注入
3.prototype范围的循环依赖
对于“prototype ”作用域 bean, Spring 容器无法完成依赖注入,因为 Spring 容器不进行缓 存“prototype
”作用域的 bean ,因此无法提前暴露一个创建中的 bean 示
所以Spring只能解决单例setter类型的循环依赖,那么是如何实现的呢?那就是大名鼎鼎的三级缓存机制:
在Spring容器的整个声明周期中,单例Bean有且仅有一个对象。这很容易让人想到可以用缓存来加速访问。
从源码中也可以看出Spring大量运用了Cache的手段,在循环依赖问题的解决过程中甚至不惜使用了“三级缓存”,这也便是它设计的精妙之处~
三级缓存其实它更像是Spring容器工厂的内的术语,采用三级缓存模式来解决循环依赖问题,这三级缓存分别指:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
...
// 从上至下 分表代表这“三级缓存”
private final Map singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
private final Map earlySingletonObjects = new HashMap<>(16); // 二级缓存
private final Map> singletonFactories = new HashMap<>(16); // 三级缓存
...
// 这个缓存也十分重要:它表示bean创建过程中都会在里面呆着~
// 它在Bean开始创建时放值,创建完成时会将其移出~
private final Set singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
// 当这个Bean被创建完成后,会标记为这个 注意:这里是set集合 不会重复
// 至少被创建了一次的 都会放进这里~~~~
private final Set alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}
注:AbstractBeanFactory继承自DefaultSingletonBeanRegistry
singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖。
获取单例Bean的源码如下:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
...
@Override
@Nullable
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
@Nullable
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;
}
...
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
protected boolean isActuallyInCreation(String beanName) {
return isSingletonCurrentlyInCreation(beanName);
}
...
}
先从一级缓存singletonObjects中去获取。(如果获取到就直接return)
如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)
如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。(如果获取到了就从singletonFactories中移除,并且放进earlySingletonObjects。其实也就是从三级缓存移动(是剪切、不是复制哦~)到了二级缓存)
加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决
getSingleton()从缓存里获取单例对象步骤分析可知,Spring解决循环依赖的诀窍:就在于singletonFactories这个三级缓存。这个Cache里面都是ObjectFactory,它是解决问题的关键。
// 它可以将创建对象的步骤封装到ObjectFactory中 交给自定义的Scope来选择是否需要创建对象来灵活的实现scope。 具体参见Scope接口 @FunctionalInterface public interface ObjectFactory{ T getObject() throws BeansException;
这里是引用经过ObjectFactory.getObject()后,此时放进了二级缓存earlySingletonObjects内。这个时候对象已经实例化了,虽然还不完美,但是对象的引用已经可以被其它引用了。
此处说一下二级缓存earlySingletonObjects它里面的数据什么时候添加什么移除???
添加:向里面添加数据只有一个地方,就是上面说的getSingleton()里从三级缓存里挪过来
移除:addSingleton、addSingletonFactory、removeSingleton从语义中可以看出添加单例、添加单例工厂ObjectFactory的时候都会删除二级缓存里面对应的缓存值,是互斥的。
依旧以上面A、B类使用属性field注入循环依赖的例子为例,对整个流程做文字步骤总结如下:
使用context.getBean(A.class),旨在获取容器内的单例A(若A不存在,就会走A这个Bean的创建流程),显然初次获取A是不存在的,因此走A的创建之路~
实例化A(注意此处仅仅是实例化),并将它放进缓存(此时A已经实例化完成,已经可以被引用了)
初始化A:@Autowired依赖注入B(此时需要去容器内获取B)
为了完成依赖注入B,会通过getBean(B)去容器内找B。但此时B在容器内不存在,就走向B的创建之路~
实例化B,并将其放入缓存。(此时B也能够被引用了)
初始化B,@Autowired依赖注入A(此时需要去容器内获取A)
此处重要:初始化B时会调用getBean(A)去容器内找到A,上面我们已经说过了此时候因为A已经实例化完成了并且放进了缓存里,所以这个时候去看缓存里是已经存在A的引用了的,所以getBean(A)能够正常返回
B初始化成功(此时已经注入A成功了,已成功持有A的引用了),return(注意此处return相当于是返回最上面的getBean(B)这句代码,回到了初始化A的流程中~)。
因为B实例已经成功返回了,因此最终A也初始化成功
到此,B持有的已经是初始化完成的A,A持有的也是初始化完成的B,完美~
站的角度高一点,宏观上看Spring处理循环依赖的整个流程就是如此。希望这个宏观层面的总结能更加有助于小伙伴们对Spring解决循环依赖的原理的了解,同时也顺便能解释为何构造器循环依赖就不好使的原因。



