Spring循环依赖

spring版本5.2.2

不考虑AOP的循环依赖

当不用考虑AOP时,加入一个二级缓存即可解决bean的循环依赖问题

推断构造函数
a) 先判断是否有 @Autowired 注解的构造方法,如有则使用该构造方法。
b) 如果有多个构造方法,并且不满足 a 条件,则默认使用无惨构造方法进行实例化创建。
c) 如果多个构造方法,并且没有无参构造方法,则抛出异常无法进行实例化创建。
实例化 的前置 处理
a) 前置处理会调用实现 MergedBeanDefinitionPostProcessor 接口的 postProcessMergedBeanDefinition 方法,如 AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition 的方法会将 所有 @Autowired 注解标注的方法或者属性封装 InjectionMetadata 对象并放 injectionMetadataCache Map 中进行缓存,以便在 调用 populateBean 方法时进行属性填充 populateBean 方法会调用实现 InstantiationAwareBeanPostProcessor 接口 postProcessProperties 方法,该方法会找到缓存 Map 中的注入点信息,反射调用进行属性填充 @Resource 注解于此类似,对应的类 CommonAnnotationBeanPostProcessor @PostConstructor 注解的注入点也 InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition 方法,此方法会将类下面的所有生命周期方法 LifecycleMetadata 对象 存入 lifecycleMetadataCache 属性 Map 中,最终 AbstractAutowireCapableBeanFactory#initializeBean 的方法中进行初始化,初始化的方法时后置处理器的调用 方法 AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization 其次 AbstractAutowireCapableBeanFactory#invokeInitMethods 方法主要是先调用实现 InitializingBean afterPropertiesSet 方法 ,最后 调用 init -method 的方法

获取bean的逻辑

       第一次创建之前会尝试去单例池里面查找。即调用DefaultSingletonBeanRegistry#getSingleton(String)方法。

此方法没有第二个参数,则会默认调用getSingleton(beanName, true)方法,即当没有从二级缓存中获取到时,会从三级缓存中获取。如果从三级缓存中获取到结果,则会调用singletonFactory.getObject()的,此次的getObject方法会触发在创建时存储三级缓存的ObjectFactorygetObject方法。

       ObjectFactory是一个lamda表达式,主要内容是getEarlyBeanRefrence方法,该方法主要是提前进行aop代理创建,并在earlyProxyReferencesput一个已经进行aopkey,防止重复被代理。

       未获取到。则会调用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#postProcessAfterInitializationAop的开启由注解@EnableAspectJAutoProxy开启。开启源码见图示。

       若出现了循环依赖场景的aop代理对象的创建时,就依然需要进行代理对象的提前创建,为了满足此场景spring又加入了三级缓存即singletonFactories ,该缓存mapvalueObjectFactory对象,spring在进行创建之前会判断是否满足循环依赖,如果出现了循环依赖就会存入一个lamda表达式,该lamda表达式包含了被代理目标对象的大部门属性,用于最后getObject方法调用时完成代理对象的创建。

       问题1:可以将bean提前进行代理处理,处理完成之后从三级缓存中移除并放入二级缓存中,如果在二级缓存中,则不进行重复创建(防止多次被代理),避免了多个对象之间的循环依赖问题。

       问题2:为什么需要三级缓存,是由于提前进行创建代理对象的时候,此时聚合的target应该是原始对象,而原始对象又没有传递进来所以无法获取。因此就需要三级缓存加入,提前将需要的原始对象放入三级缓存中,以便后续进行代理对象的创建。

Springcontext的三级缓存

1. singletonObjects 一级缓存:主要用于存储单例 bean ,防止单例 bean 的多次重复创建。
2. earlySingletongObjects 二级缓存:主要用于当有循环依赖出现,从三级缓存中创建完对象时,将三级缓存的对象移除,并存入二级缓存中,以避免对象被多次进行代理创建,比如多个需要被代理的类的循环依赖场景。
3. singletonFactories 三级缓存:主要用于解决需要 aop 时,代理对象需聚合被代理对象(此时聚合的被代理对象不是一个简单的原始对象,而是完成了代理的对象)的场景。

 Aop开启的源码步骤

 

 类图关系如上,最终会在postProcessAfterInitialization方法中判断aop是否调用,而图中earlyProxyReferences的赋值操作在对象创建之前完成,如果出现了循环依赖,此处就不会执行,证明已经提前完成了代理对象的创建。即不会进行二次Aop操作。见下图。并且会将对象信息,以lamda表达式的方式存入了三级缓存中,在判断如果出现了循环依赖并且在二级缓存中未获取到依赖的bean就会从三级缓存中获取,获取的时候会调用singletonFactory.getObject()方法,并执行lamda表达式里面的内容,并进行代理对象的创建(此时对代理对象的属性target赋值就不是原始的对象)

addSingletonFactory方法见后图

 

考虑AOP的循环依赖

如有不对的地方请多多指教

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值