spring的三级缓存
- 一级缓存
-
- SingletonObject 存放完全初始化好的bean,该缓存取出来的bean 可以直接使用
- 二级缓存
-
- earlySingletonObject 提前曝光单单例对象的cache,存放原始对象bean(尚未填充属性),用于解决循环依赖。
- 三级缓存
-
- SingletonFacotoies单例对象工厂的cache,存放 ObjectFactory对象,用于解决循环依赖。
三级缓存中 单例对象的加入时机
- 先从一级缓存中去获取,
- 一级缓存没有则去二级缓存中去取,
- 二级缓存中如果没有,则去3级中找。3级缓存没有则创建 ObjectFactory对象。
- 如果三级缓存中有则剪切到二级缓存。这样做的目的是,在init()时加载的是同一个对象。
- init 之后,则剪切到 一级缓存。
是否可以没有二级缓存
如果是两个对象产生循环依赖,可以不要二级缓存。但是如果是3个就有问题了
A 注入到C,A注入到B,又需要从第三级缓存中获取实例,而第三级缓存里保存的并非真正的实例对象,而是ObjectFactory对象。说白了,两次从三级缓存中获取都是ObjectFactory对象,而通过它创建的实例对象每次可能都不一样的。
为了解决这个问题,spring引入的第二级缓存。其实A对象的实例已经被添加到第二级缓存中了,而在A注入到C时,只用从第二级缓存中获取该对象即可。
@Service
public class A {
@Autowired
private C c;
@Autowired
private B b;
....
}
@Service
public class B {
@Autowired
private A a;
....
}
@Service
public class C {
@Autowired
private A a;
....
}
是否需要三级缓存
如果创建的Bean有对应的代理,那其他对象注入时,注入的应该是对应的代理对象;但是Spring无法提前知道这个对象是不是有循环依赖的情况,而正常情况下(没有循环依赖情况),Spring都是在创建好完成品Bean之后才创建对应的代理。这时候Spring有两个选择:
不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。
不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖`的情况下,Bean就可以按着Spring设计原则的步骤来创建。
Spring选择了第二种方式,那怎么做到提前曝光对象而又不生成代理呢?
Spring就是在对象外面包一层ObjectFactory,提前曝光的是ObjectFactory对象,在被注入时才在ObjectFactory.getObject方式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map<String, Object> earlySingletonObjects。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));