下面说Spring的三级缓存
// 从上至下 分表代表这“三级缓存”
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); // 三级缓存
...
// 这个缓存也十分重要:它表示bean创建过程中都会在里面呆着,它在Bean开始创建时放值,创建完成时会将其移出
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
// 当这个Bean被创建完成后,会标记为这个 注意:这里是set集合 不会重复。至少被创建了一次的 都会放进这里
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
实现循环依赖的原理
Spring的循环依赖的理论依据基于Java的引用传递,获得对象的引用时,对象的属性是可以延后设置的。
一、如果没有2级缓存,只有一级和三级的情况下,可以吗?
不可以,首先要明白,一次缓存的是完全实例化好的Bean,二级缓存是半成品,三级缓存是ObjectFactory
如果去掉了二级缓存,假设A依赖B,B依赖A,当实例化B的时候,发现三级缓存有A的ObjectFactory对象,然后拿到A的实例对象,拿完只有必须要保证其他依赖的Bean是一个实例,多次调用产生的对象肯定是不一样的(前提是A是AOP代理的Bean),所以一定要把A从三级转移出来,如果只有一级缓存,那么放里面肯定是不合适的。所以不能少二级缓存。
二、如果没有三级缓存,只有一级缓存和二级缓存,可以吗?
如果只是普通Bean之间的循环依赖是用不上三级缓存,只有AOP代理的Bean会用到三级缓存。
那么如果去掉三级缓存也是可以的,下面举个例子说明下。比如A和B都是需要AOP代理并且存到循环依赖的Bean。
流程:
1、先创建A的普通实例对象,在通过后置处理器来创建A的代理对象,此时A的代理对象还没有完成初始化完,所以要存到如二级缓存里面去,在去填充A里面的属性,发现有依赖B,此时去实例化B,
2、创建B普通实例对象,在通过后置处理器创建B的代理对象,B也没有完全实例完,所以存到二级缓存里面去,在去填充B里面的属性,发现依赖A,从二级缓存里面拿到A的代理对象赋值。到这里B的代理对象创建完成,并把B存到一级缓存里面,移除二级缓存中的B。在回到A的属性填充流程上。
3、此时A就可以拿B的代理对象,并完成填充,流程结束。
这么做可以但是站在性能上不合适,就是发现是AOP代理对象,直接生成然后再去实例化,不使用考虑懒加载的方式处理。