什么是循环依赖
相互依赖,A类对象中有B类属性,B类对象中有A类属性。两者相互引用,依赖。
// A依赖了B
class A{
public B b;
}
// B依赖了A
class B{
public A a;
}
如何关闭循环依赖
public class TestApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
// 设置不允许循环依赖
beanFactory.setAllowCircularReferences(false);
applicationContext.register(AppConfig.class);
applicationContext.refresh();
}
}
循环依赖的条件
1.依赖注入的方式不能全是构造器注入
2.容器允许循环依赖
Spring循环依赖解决描述
生命周期
1。创建原始对象
2。填充属性
3。执行初始化后后置处理器(aop)
4。添加到单例池
循环步骤解析
只有一个Map
按照如下分析,如果只需要一个map(单例池),是会出现死循环的。
两个Map
如果使用两个Map,一个放单例bean,一个放原始对象,解决。
看样子似乎满足了,但是上面存在最大的问题,如果A类使用了AOP,执行完填充属性后,然后执行初始化后后置处理器步骤,可能A类最后放到单例bean中的是代理对象,而B对象持有的A类引用是原始对象,所以只能在创建原始对象中放代理对象到Map中(也就是提前AOP),这样就能解决后面的代理对象。
优化两个Map-v1
看样子满足了,但是又出来一个问题,并不是所有的类都需要代理对象(也就是需要得到提前AOP的代理对象),那么存A的代理对象到Map就存在动态,可能放的是原始对象或者是代理对象。那么只有在从第二个map获取A的时候,采取判断当前是需要原始对象还是代理对象,这个逻辑明显需要一个函数来实现,所以可优化用lambda,在需要的时候获取执行即可。
优化两个Map-v2
现在来看已经满足了,能够满足如果产生循环依赖,如果需要提前aop或者不进行aop的属性填充。
但是存在一个问题,如果A类中又来了一个C类型属性C填充,也产生了循环依赖,那么如何?很明显会类似属性B填充的逻辑,但是到最后一个步骤,又会重新执行一次lambda的表达式获取A的原始对象或者A的代理对象,但是在注入属性B的时候已经执行一遍了,这里就没有必要重新执行一遍了生成一个新的aop对象,所以需要将这个lambda表达式的结果缓存到一个map中,也就是使用第三个map,后面再循环注入直接先从第三个map中获取是否存在。
优化三个Map-v3
具体看填充属性C.先从单例map中获取A,如果不存在,则判断第三个map中是否存在A的代理对象或者原始对象,如果没有,则从第二个map中获取A的lambda表达式执行获取A的代理对象或者原始对象。
由此,如果完成循环依赖,至少需要三个Map,一个存放单例bean,一个放lambda表达式,另一个缓存提前aop或者原始对象。
源码分析
定义的三个Map
/** Cache of singleton objects: bean name to bean instance. */
//存放单例bean 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
//放lambda表达式 二级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
//缓存提前aop或者原始对象 三级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
创建原始对象,添加lambda(三级缓存Map)
addSingletonFactory()方法的内部实现
注意在添加这个方法之前,有一个判断,判断当前beanDefinition的是不是定义的单例,是否允许循环依赖以及当前beanName是不是在正在创建中集合(isSingletonCurrentlyInCreation(beanName)
) 只有三者成立的情况下才会添加。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
// 如果当前bean不存在单例
if (!this.singletonObjects.containsKey(beanName)) {
//添加lambda
this.singletonFactories.put(beanName, singletonFactory);
// 删除lambda执行后的缓存的对象,就算不存在
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
从第三个Map中获取(二级缓存Map)
会发现首先从一级缓存中拿,如果一级缓存不存在且在创建中。从二级缓存Map拿,如果不存在,会调用lambda表达式调用获取。
问题
提前进行了aop,后面初始化后后置处理器如何判断已经提前了?
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference
因为在调用第三级缓存的lambda表达式的时候,会将当前提前进行aop的beanName缓存起来,使用earlyProxyReferences
,类型是Map.
// 出现了循环依赖
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName); // beanName aService
// earlyProxyReferences 会用来判断当前bean是否还需要进行aop
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey); //返回代理对象
}
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
在执行初始化后后置处理器的postProcessAfterInitialization()方法时会使用earlyProxyReferences
来判断是否已经提前进行了aop。
@Override
// 正常情况进行AOP的地方
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// earlyProxyReferences中存的是哪些提前进行了AOP的bean,beanName:AOP之前的对象
// 注意earlyProxyReferences中并没有存AOP之后的代理对象 BeanPostProcessor
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 没有提前进行过AOP,则进行AOP
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
// 为什么不返回代理对象呢? 因为是null,无需处理
return bean; //
}
为什么在下表中的第二种情况的循环依赖能被解决,而第四种情况不能被解决呢?
依赖描述 | 依赖注入方式 | 循环依赖是否被解决 |
AB相互依赖(循环依赖) | 均采用setter方法注入 | 是 |
AB相互依赖(循环依赖) | A中注入B的方式为setter方法,B中注入A的方式为构造器 | 是 |
AB相互依赖(循环依赖) | 均采用构造器注入 | 否 |
AB相互依赖(循环依赖) | B中注入A的方式为setter方法,A中注入B的方式为构造器 | 否 |
Spring在创建Bean时默认会根据自然排序进行创建,所以A会先于B进行创建。
@Component
public class A {
@Autowired
public A(B b){
}
}
@Component
public class B {
@Autowired
private A a;
}
运行结果如图:
那么有解决方案吗? 有的,在注入B的时候,注入代理即可。
@Component
public class A {
private B b;
@Autowired
@Lazy
public A(B b){
this.b=b;
}
}
最后结果分析图
总结
循环依赖至少三个Map;
1.单例池(一级缓存)
2.缓存代理对象或者原始对象(二级缓存)
3.Lambda表达式Map(三级缓存)
4.earlyProxyReferences
属性Map记录是否提前进行了AOP.
5.singletonsCurrentlyInCreation
记录当前正在创建的beanName