spring的循环依赖解决方案原理
上图分为两个步骤,比如当前需要创建对象A,那么初始化完成后(调用完构造方法),会将对象加入到三级缓存中,也就是上面addSingletonFactory 会使用一个lamda表达式构造一个ObjectFactory放入到singletonFactories(三级缓存),然后开始进行属性注入,比如说需要注入一个对象B,这个时候开始执行箭头指的第二段代码populateBean,然后初始化对象B,同时也加入singletonFactories(三级缓存),然后开始进行属性注入,而且需要进行注入属性对象是A这个时候就发生循环依赖了。
现在开始进行注入属性A了,首先会去容器里面找,会找遍所有的缓存,一级缓存,二级缓存,三级缓存,最后这个对象显然在一二级缓存是没有的,只能在三级缓存中找到当时加入lamda表达式构造一个ObjectFactory,然后调用这个lamda表达式的getObject方法,其实也就是调用getEarlyBeanReference
可以看到getEarlyBeanReference其实内部需要遍历执行一些BeanPostProcessor,其实包含一个叫做AnnotationAwareAspectJAutoProxyCreator的BeanPostProcessor,然后执行这个类的getEarlyBeanReference
可以看到这里这里会传入原始创建的bean,首先会尝试有没有和这个bean相关的切面,如果找到了,那么会返回代理bean,如果没有找到,那么就将之前缓存的bean直接返回,这样就解决了循环依赖问题
spring能解决哪些循环依赖和不能解决
能解决情况
能解决属性注入的情况,构造方法的方式解决不了(因为三级缓存是在构造方法之后加入的,属性注入之前加入的)
能解决事务代理生成的对象(因为发生在三级缓存创建最终对象,放入了二级缓存中,后续再也没有别的BeanPostProcess干预生成了)
不能解决情况
1.通过构造方法进行注入(发生在三层缓存之前)
2.异步方法情况,创建代理的对象和二级缓存的对象不是同一个对象(发生在注入属性之后)
报错:This means that said other beans do not use the final version of the bean(bean的版本变了). This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example
分析原因:
我们知道每个对象属性注入前都会加入到三级缓存当中,从三级缓存获取对象后,进行确认是否要创建代理对象,确认完后,就是最终的一个对象了,然后放入二级缓存中,但是异步注解会生成一个代理对象,但是发生的时机是属性注入后进行,把原来确认过的二级缓存对象覆盖了,再次生成一个代理对象,使得最后校验的时候,发现三级缓存确认的对象(已经放入二级缓存),不是同一个对象,使得报错了。(三级缓存创建代理对象时候有AnnotationAwareAspectJAutoProxyCreator,但是没有AsyncAnnotationBeanPostProcessor导致 异步注解的对象在属性注入后才开始创建,只有实现了SmartInstantiationAwareBeanPostProcessor接口的beanPostprocess才会在三级缓存创建代理的时候可用)
spring循环依赖总结
循环依赖解决是发生在三级缓存的,原始对象和事务代理对象都在这个三级缓存地方处理,处理完成后放到二级缓存中,如果发生在构造或者属性注入完成后,那么是解决不了会报错的。构造是发生在三级缓存之前,异步是发生在三级缓存之后,属性注入完,然后才生成的代理对象,进行了覆盖,在二次缓存检查的时候检查出来不是最终版本对象,然后报错了