|Spring循环依赖,我本来都不想写的,但网上好多错误观点( 三 )


  1. 实例化
  2. 属性注入
  3. 初始化
在实例化 Bean 之后 ,会往 singletonFactories 塞入一个工厂 , 而调用这个工厂的 getObject 方法 , 就能得到这个 Bean。
addSingletonFactory(beanName () -> getEarlyBeanReference(beanName mbd bean));

要注意 , 此时 Spring 是不知道会不会有循环依赖发生的 ,但是它不管, 反正往 singletonFactories 塞这个工厂 , 这里就是 提前暴露。
然后就开始执行属性注入 , 这个时候 A 发现需要注入 B , 所以去 getBean(B) , 此时又会走一遍上面描述的逻辑 , 到了 B 的属性注入这一步 。
此时 B 调用 getBean(A) , 这时候一级缓存里面找不到 , 但是发现 A 正在创建中的 , 于是去二级缓存找 , 发现没找到 , 于是去三级缓存找 , 然后找到了 。
并且通过上面提前在三级缓存里暴露的工厂得到 A , 然后将这个工厂从三级缓存里删除 , 并将 A 加入到二级缓存中 。
然后结果就是 B 属性注入成功 。
紧接着 B 调用 initializeBean 初始化 , 最终返回 , 此时 B 已经被加到了一级缓存里。
这时候就回到了 A 的属性注入 , 此时注入了 B , 接着执行初始化 , 最后 A 也会被加到一级缓存里 , 且从二级缓存中删除 A 。
Spring 解决依赖循环就是按照上面所述的逻辑来实现的 。
重点就是在对象实例化之后 , 都会在三级缓存里加入一个工厂 , 提前对外暴露还未完整的 Bean , 这样如果被循环依赖了 , 对方就可以利用这个工厂得到一个不完整的 Bean , 破坏了循环的条件 。
为什么循环依赖需要三级缓存 , 二级不够吗?上面都说了那么多了 , 那我们思考下 , 解决循环依赖需要三级缓存吗?
很明显 , 如果仅仅只是为了破解循环依赖 , 二级缓存够了 , 压根就不必要三级 。
你思考一下 , 在实例化 Bean A 之后 , 我在二级 map 里面塞入这个 A , 然后继续属性注入 。
发现 A 依赖 B 所以要创建 Bean B , 这时候 B 就能从二级 map 得到 A, 完成 B 的建立之后 ,A 自然而然能完成 。
所以 为什么要搞个三级缓存 , 且里面存的是创建 Bean 的工厂呢 ?
我们来看下调用工厂的 getObject 到底会做什么 , 实际会调用下面这个方法:
protected Object getEarlyBeanReference(String beanName RootBeanDefinition mbd Object bean) {    Object exposedObject = bean;    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {            exposedObject = bp.getEarlyBeanReference(exposedObject beanName);
       
       return exposedObject;


重点就在中间的判断 , 如果 false , 返回就是参数传进来的 bean , 没任何变化 。
如果是 true 说明有
InstantiationAwareBeanPostProcessors。
且循环的是 smartInstantiationAware 类型 ,如有这个 BeanPostProcessor 说明 Bean 需要被 aop 代理。
我们都知道如果有代理的话 , 那么我们想要直接拿到的是代理对象 。
也就是说如果 A 需要被代理 , 那么 B 依赖的 A 是已经被代理的 A , 所以我们不能返回 A 给 B , 而是返回代理的 A 给 B 。
这个工厂的作用就是判断这个对象是否需要代理 , 如果否则直接返回 , 如果是则返回代理对象 。
看到这明白的小伙伴肯定会问 , 那跟三级缓存有什么关系 , 我可以在要放到二级缓存的时候判断这个 Bean 是否需要代理 , 如果要直接放代理的对象不就完事儿了 。