问题来源:
A类中有成员变量B类的对象。
B类中也有成员变量A类的对象。
// 简单来说就,就是如下情况
class A {
@Autowire
B b;
}
class B {
@Autowire
A a;
}
在一开始的开始
也就是程序第一次获取A的实例时,需要从spring容器单例池中获取。但此时单例池中还没有A的实例,所以要new 一个A对象。
在A实例 创建和初始化的时候,要获取成员变量b的实例,这也需要从单例池中获取,但此时B类在池子中也没有对象,也需要new一个。
在B实例 创建和初始化的时候,要获取成员变量a的实例,这就造成了在创建的时候循环依赖,都需要对方一个已经创建好的实例。
解决思路
一个单例池肯定是不够的,增加一个半成品池,也就是二级缓存的机制。
在第一步A实例化之后,将A的半成品实例放到这个半成品池子当中,这样子,当B初始化的时候,直接从半成品池子获取A的半成品实例,等B初始化完成之后,再完成A之后初始化操作。
最后将A的实例从半成品 转移到 单例池中,即可解决 循环依赖的问题。
但是,在AOP的时候,仅靠二级缓存是不行的!!!
在AOP的情景, A的成员变量b,不是B的实例,而是B的动态代理实例 ,可以看做 $b。
而B的成员变量a,也不是A的实例,而是A的动态代理实例,可以看做 $a.
如果继续采用二级缓存的话,半成品池子里面放的只有 实例,而不是 动态代理实例,是行不通的。
需要一个 工厂池,也就是三级缓存的机制,用来获取A的代理对象。
在上面步骤中,A实例创建时,在工厂池 中添加一个 半成品的a。
然后在初始化的时候需要一个B的动态代理实例,于是走B的创建初始化流程。
B实例创建时,也会在工厂池中添加一个半成品的b,
然后B初始化的时候,从工厂池中获取半成品a,并调用他的提前引用方法,获取到a的动态代理对象,也就是$a.
将动态代理实例 $a 放入到 半成品池子里面去。再用半成品池里面 $a 去填充B实例里面的成员变量a。
完成后续B的初始化操作之后,调用b对象的后置处理,创建动态代理实例 $b,并将$b 放入到 单例池里面去,B的实例化操作便完成了。
B完成之后,又回到A的初始化后续步骤,直接将$b 从单例池中赋值给A的成员变量即可。
当然,同样会调用A的后置处理,但因为前面通过提前引用的方式,已经创建了A的动态代理实例$a,所以后置处理并不会再去创建 A的动态代理实例,此时直接将$a 从半成品池中迁移到单例池即可。
Spring 解决循环依赖的思路基本就这样了,三级缓存机制。