关键:主要是为了解决循环依赖问题
一、产生循环依赖原因
public class A {
private B b;
}
public class B {
private A a;
}
在Spring中初始化一个Bean并不是简单的new A(),需要经过属性注入以及各种后置处理器的处理。
1:当我们初始化A的时候,在属性注入会发现需要注入一个对象B
2:此时Spring就会去Bean容器中找是不是有这个B对象,如果有就返回
3:如果没有就会初始化B,此时在对B进行属性注入的时候发现需要A,又要去初始化A
4:此时就造成了循环依赖的情况
二、如何解决
因为创建对象分成了初始化、填充属性、得到可用对象,有了这些步骤,所以才能解决。
Spring是使用三级缓存来解决循环依赖的,准确来说应该是四层
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
...
// 从上至下 分表代表这“三级缓存”
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存
...
}
singletonObjects:缓存已经初始化好的Bean
earlySingletonObjects:缓存正在初始化的Bean
singletonFactories:缓存的创建Bean的ObjectFactory,表示对象工厂,表示用来创建早期bean对象的工厂。
B 在创建对象的时候,发现A在一级缓存里没有,就去二级缓存找。二级也没就去三级,都没有就通知创建。
Spring 再创建 A 的时候,先创建一个半成品 A,不填充属性 B。这个时候,B从二级缓存中拿到A,放到自己的属性填充。完成创建对象过程。这个时候 B 创建完成,A 填充属性就可以直接用 B 。
三、二层已经够了,为什么需要三层
在上面描述的过程中,其实二级缓存已经够了。为什么Spring要用三级缓存?
因为还有特殊情况,比如,如果 A 经过了AOP,那么再 B 填充的时候,我们就需要拿到 A 代理后的对象。
我们都知道动态代理的执行再填充属性后的 BeanPostProcessor 的after里去执行,这个时候是需要填充属性完成才可以执行。这就导致给B的只能是原始对象A,这就不对了。这个时候三级缓存就出来了
四、singletonFactories三级缓存的作用
singletonFactories 中存的是某个 beanName 对应的 ObjectFactory,在bean的生命周期中,生成完原始对象之后,就会构造一个 ObjectFactory存入 singletonFactories 中。这个 ObjectFactory是一个函数式接口,所以支持 Lambda 表达式: () -> getEarlyBeanReference( beanName , mbd , bean )。主要就是传入一个对象,返回这个对象的代理对象。
也就是说在初始化A的时候,在属性注入之前会提前创建A对象的ObjectFactory放到这个singletonFactories中,后续B对象在需要A对象的时候就会调用这个 () -> getEarlyBeanReference( beanName , mbd , bean ) 来获取代理后的 A 对象。此时如果这个 A 对象不需要经过AOP代理的就会生成一个普通对象,如果后续需要经过AOP代理此时就会生成一个代理对象返回给B进行属性注入。
这个时候就会有下一个问题,如果 A 设置是单例的,就会有下面问题:
假设这个A对象是需要经过AOP代理生成一个代理对象的,B对象使用的也是A的代理对象,看起来似乎没什么问题。
但是AOP代理是在初始化之后执行的,也就是说你现在虽然提前生成了A的代理对象,但是在初始化A对象之后还要经过AOP的一次代理,那么作为单例来说,这二个代理对象是不一样的。
五、第四级别缓存 - earlyProxyReferences
在经过 () -> getEarlyBeanReference(beanName,mbd,bean) 得到的对象如果是经过AOP代理的对象就会放到这个 earlyProxyReferences 中,这样后续在初始化后置处理器中只要判断earlyProxyReferences中有没有这个Bean,如果有就不用再次做代理。
六、总结
Spring使用三级缓存来解决循环依赖问题,第一级缓存用于存储已经初始化好的Bean对象,第二级缓存用于存储正在初始化的Bean对象,第三级缓存用于存储创建Bean对象的ObjectFactory对象。然后在特殊情况下,如果对Bean对象进行动态代理,则由第三级来解决问题。第四级缓存用于存储经过AOP代理的对象,以解决单例问题。