一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情。
spring的循环依赖是什么
假设有A,B两个类,A类有B属性,B类有A属性,那么当spring扫描到这两个类,如何对这两个类进行实例化
public class A{
B b;
}
public class B{
A a;
}
如上面代码,A对象有一个属性是B,B对象有一个属性是A,当A对象生成的时候依赖B对象,B对象生成的时候需要依赖A对象,这个时候就很容易形成一个闭环,如果这样一直死循环下去。
循环依赖的解决
Spring内部通过三级缓存来解决循环依赖的问题
所谓三级缓存,就是spring容器解决循环依赖的三个map:
一级缓存:singletonObjects,是一个ConcurrentHashMap,也叫单例池,存放已经经历了完整生命周期的Bean对象;
二级缓存:earlySingletonObjects,是一个HashMap,存放早期暴露出来的Bean对象,Bean的生命周期未结束,属性还未填充完整;
三级缓存:singletonFactories,第三级缓存value是一个工厂,三级缓存存放可以生成bean的工厂。
注意:只有单例的Bean会通过三级缓存来解决循环依赖的问题,而非单例的Bean,每次从容器获取都是一个新的对象,都会重新创建,所以非单例的Bean是没有缓存的,不会放在三级缓存中。
源码分析
Spring通过AbstractBeanFactory#doGetBean来获取Bean对象,尝试去缓存中获取对象:一级>二级>三级
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
...
//创建单例bean
if (mbd.isSingleton()) {
//此处getSingleton不同于上面的getSingleton方法:
//前面的getSingleton主要是从缓存中获取对象,如果有直接返回;
//后面的getSingleton获取单例对象,用于触发构建bean
sharedInstance = getSingleton(beanName, () -> {
try {
//进入创建bean的逻辑
return createBean(beanName, mbd, args);
}
...
return (T) bean;
}
通过AbstractBeanFactory#getSingleton方法,最终会调用DefaultSingletonBeanRegistry#getSingleton ,尝试从缓存中获取,
先从一级缓存中获取,一级缓存中没有获取到并且该bean处于正在创建阶段,去二级缓存中获取,二级缓存中也没有获取到对象,且allowEarlyReference为true,直接从三级缓存中获取ObjectFactory对象,把对象放在二级缓存,并且ObjectFactory包装对象从三级缓存中删除掉
因为第一次去缓存中获取不到对象,所以还会走下面的getSingleton–>createBean–>doCreateBean逻辑。
在createBean之前把Bean标记成正在创建,作为循环依赖的出口:
DefaultSingletonBeanRegistry#addSingletonFactory:用于把对象包装成ObjectFactory暴露到三级缓存singletonFactories中,解决循环依赖