spring解决循环依赖使用了三级缓存,三级缓存分别为:
类:DefaultSingletonBeanRegistry
/** Cache of singleton objects: bean name to bean instance. */
//一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
//二级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. */
//三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
@FunctionalInterface
public interface ObjectFactory<T> {
/**
* Return an instance (possibly shared or independent)
* of the object managed by this factory.
* @return the resulting instance
* @throws BeansException in case of creation errors
*/
T getObject() throws BeansException;
}
一级缓存存储成品对象
二级缓存存储半成品对象
三级缓存存储函数式接口ObjectFactory(lambda表达式) 当调用接口的getObject方法时才会执行接口中的逻辑。
向三级缓存中存储的具体方法如下:
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
addSingletonFactory方法中传入了beanName以及函数式接口,接口中调用了getEarlyBeanReference方法。
addSingletonFactory方法如下:
(如果一级缓存中该beanName已经存在,表示该bean已经实例化完成,可以使用,如果以及缓存中不存在,则将该函数式接口存在三级缓存中,等待合适的时机调用->)
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
getEarlyBeanReference方法如下:
(从getEarlyBeanReference方法层层递进可以看到底层实现是JDK和cglib的动态代理)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
即调用getObject()方法时会执行getEarlyBeanReference方法!!!
循环依赖的问题:
1.三个依赖的读取顺序是什么?
先读一级缓存,再读二级缓存,最后读取三级缓存
2.为什么存二级缓存时要把三级缓存删掉,存一级缓存时要把二级缓存删掉?
因为能从一级缓存中读到数据时,就没有必要再去读二级和三级了,二级同理。
3.三个缓存中存储的都是什么对象?
一级缓存:成品对象
二级缓存:半成品对象
三级缓存:lambda表达式
4.用一个缓存能解决循环依赖的问题吗?
不能,因为对于同一个对象来说,这一个map中既要存储该对象的成品对象,又要存储该对象的半成品对象,从map中读取对象时,无法判断获取的是成品对象,还是半成品对象,所以一个缓存无法解决循环依赖的问题。
5.用两个缓存能解决循环依赖的问题吗?
可以解决循环依赖的问题,但是有个前提条件:运行过程中不会创建代理对象,则两个缓存可以解决循环依赖的问题,如果运行过程中会创建代理对象,则必须使用三级缓存。
6.为什么使用三级缓存能解决问题?***
- 创建代理对象是在哪个步骤?在beanPostProcessor的after方法中会完成动态代理对象的创建
- 创建代理对象时是否需要原始对象?需要
- 根据上面两个前提条件,我们发现一个问题:刚开始的时候创建了原始对象,然后再根据原始对象创建了代理对象,在对外暴露对象时应该向外暴露原始对象还是代理对象?
- 针对上面的问题,spring给出的方案是在生成代理对象时,使用代理对象覆盖原始对象。