循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。类似于死锁
Spring中循环依赖场景有:
(1)构造器的循环依赖 (不能被解决,加入singletonFactories
三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决
(2)field属性的循环依赖,也就是setter注入的循环依赖。(可以通过三级缓存来解决)
Spring循环依赖的理论依据其实是Java基于引用传递,当我们获取到对象的引用时,对象的field或者或属性是可以延后设置的。
Spring单例对象的初始化其实可以分为三步:
@Autowired
)initMethod
、InitializingBean
等方法/** Cache of singleton objects: bean name --> bean instance */
private final Map singletonObjects = new ConcurrentHashMap(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map> singletonFactories = new HashMap>(16);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map earlySingletonObjects = new HashMap(16);
singletonFactories
三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决 ,工厂对象中,有3个field,一个是beanName,一个是RootBeanDefinition,一个是已经创建好的,但还没有注入属性的bean,如果在2级缓存中,还是没找到,则在3级缓存中查找对应的工厂对象,利用拿到的工厂对象,去获取包装后的bean,或者说,代理后的bean。
// allowEarlyReference: 是否允许从singletonFactories中通过getObject拿到对象
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// isSingletonCurrentlyInCreation()判断当前单例bean是否正在创建中,也就是没有初始化完成
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);
}
因为原型模式每次都是重新生成一个全新的bean,根本没有缓存一说。这将导致实例化A完,填充发现需要B,实例化B完又发现需要A,而每次的A又都要不一样,所以死循环的依赖下去。唯一的做法就是利用循环依赖检测
,发现原型模式下存在循环依赖并抛出异常
可以 Bean在创建的时候给其打个标记,如果递归调用回来发现正在创建中的话--->即可说明循环依赖。
一级缓存的问题在于,就1个map,里面既有完整的已经ready的bean,也有不完整的,尚未设置field的bean。
如果这时候,有其他线程去这个map里获取bean来用怎么办?拿到的bean,不完整,怎么办呢?属性都是null,直接空指针了。
所以,我们就要加一个map,这个map,用来存放那种不完整的bean。
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
getEarlyBeanReference的作用:调用SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference()这个方法 否则啥都不做,也就是给调用者个机会,自己去实现暴露这个bean的应用的逻辑~~~,比如在getEarlyBeanReference()里可以实现AOP的逻辑~~~ 参考自动代理创建器AbstractAutoProxyCreator 实现了这个方法来创建代理对象
Spring
容器会将每一个正在创建的Bean 标识符放在一个 singletonsCurrentlyInCreation“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,而对于创建完毕的Bean将从当前创建Bean池
中清除掉。