spring版本5.2.2
不考虑AOP的循环依赖
当不用考虑AOP时,加入一个二级缓存即可解决bean的循环依赖问题
获取单例bean的逻辑
第一次创建之前会尝试去单例池里面查找。即调用DefaultSingletonBeanRegistry#getSingleton(String)方法。
此方法没有第二个参数,则会默认调用getSingleton(beanName, true)方法,即当没有从二级缓存中获取到时,会从三级缓存中获取。如果从三级缓存中获取到结果,则会调用singletonFactory.getObject()的,此次的getObject方法会触发在创建时存储三级缓存的ObjectFactory的getObject方法。
ObjectFactory是一个lamda表达式,主要内容是getEarlyBeanRefrence方法,该方法主要是提前进行aop代理创建,并在earlyProxyReferences中put一个已经进行aop的key,防止重复被代理。
未获取到。则会调用getSingleton(java.lang.String, ObjectFactory<?>)方法。
上述getSingleton的方法会先调用beforeSingletonCreation方法,该方法会DefaultSingletonBeanRegistry#singletonsCurrentlyInCreation属性的set集合中放入beanName表示自己正在被创建,在创建结束时会从set集合中移除。
中间会调用singletonFactory.getObject(),此时的getObject方法会触发createBean方法进行bean的创建。
当遇到Aop时,由于代理对象会聚合一个原始对象的属性target,而二级缓存池中的提前存入缓存的bean是原始的对象类型,而不是所需要的代理对象。因此此时的二级缓存并不能满足这种循环依赖的场景。
由于正常的Aop代理创建环节在bean的初始化后。即在AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, RootBeanDefinition)方法中的AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization方法进行处理,该方法会调用实现了BeanPostProcessor接口的postProcessAfterInitialization,即AbstractAutoProxyCreator#postProcessAfterInitialization。Aop的开启由注解@EnableAspectJAutoProxy开启。开启源码见图示。
若出现了循环依赖场景的aop代理对象的创建时,就依然需要进行代理对象的提前创建,为了满足此场景spring又加入了三级缓存即singletonFactories ,该缓存map的value为ObjectFactory对象,spring在进行创建之前会判断是否满足循环依赖,如果出现了循环依赖就会存入一个lamda表达式,该lamda表达式包含了被代理目标对象的大部门属性,用于最后getObject方法调用时完成代理对象的创建。
问题1:可以将bean提前进行代理处理,处理完成之后从三级缓存中移除并放入二级缓存中,如果在二级缓存中,则不进行重复创建(防止多次被代理),避免了多个对象之间的循环依赖问题。
问题2:为什么需要三级缓存,是由于提前进行创建代理对象的时候,此时聚合的target应该是原始对象,而原始对象又没有传递进来所以无法获取。因此就需要三级缓存加入,提前将需要的原始对象放入三级缓存中,以便后续进行代理对象的创建。
Spring的context的三级缓存
Aop开启的源码步骤
类图关系如上,最终会在postProcessAfterInitialization方法中判断aop是否调用,而图中earlyProxyReferences的赋值操作在对象创建之前完成,如果出现了循环依赖,此处就不会执行,证明已经提前完成了代理对象的创建。即不会进行二次Aop操作。见下图。并且会将对象信息,以lamda表达式的方式存入了三级缓存中,在判断如果出现了循环依赖并且在二级缓存中未获取到依赖的bean就会从三级缓存中获取,获取的时候会调用singletonFactory.getObject()方法,并执行lamda表达式里面的内容,并进行代理对象的创建(此时对代理对象的属性target赋值就不是原始的对象)
addSingletonFactory方法见后图
考虑AOP的循环依赖
如有不对的地方请多多指教