组里有一个二方库TEST,通过实现BeanPostProcessor来对bean进行拦截,同时,在拦截的过程中对bean进行手动的aop代理,但是在开发环境中,当被代理的bean被循环依赖时,会初始化异常,特此debug一下
这篇文章会涉及到springbean的生命周期,aop,循环依赖
先导知识
一级缓存
singletonObjects
,初始化完成二级缓存
earlySingletonObjects
,实例化完成(用于循环依赖)三级缓存
singletonFactory
,其他实例化操作
Spring获得实例的三种bean:
- bean:原始bean
- exposedObject:扩展bean
- earlySingletonReference:从前两个缓存中拿到的bean(如果参数为true则有第三个缓存),提前暴露的循环依赖
假如说是类a被代理,同时a引用b,b也引用a,那么源码如下:
源码分析
首先获取实例a
beanFactory.preInstantiateSingletons()
->AbstractBeanFactory#getBean
->AbstractBeanFactory#doGetBean
DefaultSingletonBeanRegistry#getSingleton(String,true)
singletonObjects
这个缓存中没存,并且这个bean没有在创建中,所以不走这个分支 ❌
AbstractBeanFactory#markBeanAsCreated
标记bean正在创建中DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>)
AbstractAutowireCapableBeanFactory#createBean
创建Bean实例AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
bean实例化前操作,(可扩展,此时用户可以提前创建该对象,如果创建对象,则返回)❌AbstractAutoProxyCreator#postProcessBeforeInstantiation
可以创建代理对象,但是用注解的时候没有创建代理对象
AbstractAutowireCapableBeanFactory#doCreateBean
进入刚才函数式接口的表达式:真正创建beanAbstractAutowireCapableBeanFactory#createBeanInstance
实例化bean,此时用BeanWrapper包裹beanDefaultSingletonBeanRegistry#addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)
增加单例工厂,为aop(创建proxy)循环依赖做准备,三级缓存AbstractAutowireCapableBeanFactory#populateBean
填充bean实例- 实例化Bean后置操作,如果显示实例化true,则直接返回 ❌
AutowiredAnnotationBeanPostProcessor#postProcessProperties
@autowired注解通过该方法对属性进行注入- 获取到依赖的属性b(转2)
AbstractAutowireCapableBeanFactory#initializeBean
初始化bean,获得扩展的exposedObject,此时没有扩展AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
初始化之前的扩展处理AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
初始化之后的扩展处理- 没有扩展,所以bean==exposedObject / 对于TEST来说,此时exposedObject=proxy,且里面的参数没有被填充
DefaultSingletonBeanRegistry#getSingleton(String,false)
从一级缓存和二级缓存中获取对象赋值给earlySingletonReference
。此时第二个缓存中有该实例,且存的是proxy / 其他的存的是原始bean- 因为bean==exposeObject,所以直接把proxy返回 / 对于TEST来说,此时bean!=exposedObject,同时系统检测该bean已经被其他bean利用,所以抛出异常
- 将该bean放入一级缓存,并将bean从二级缓存中删掉
获取实例b
AbstractBeanFactory#getBean
->AbstractBeanFactory#doGetBean
获取属性依赖DefaultSingletonBeanRegistry#getSingleton(String,true)
singletonObjects
这个缓存中没存,并且这个bean没有在创建中,所以不走这个分支 ❌
AbstractBeanFactory#markBeanAsCreated
标记bean正在创建中DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>)
AbstractAutowireCapableBeanFactory#createBean
创建Bean实例AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
bean实例化前操作,(可扩展,此时用户可以提前创建该对象,如果创建对象,则返回)❌AbstractAutoProxyCreator#postProcessBeforeInstantiation
可以创建代理对象,但是用注解的时候没有创建代理对象
AbstractAutowireCapableBeanFactory#doCreateBean
进入刚才函数式接口的表达式:真正创建beanAbstractAutowireCapableBeanFactory#createBeanInstance
实例化bean,此时用BeanWrapper包裹beanDefaultSingletonBeanRegistry#addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)
增加单例工厂,为aop(创建proxy)循环依赖做准备,三级缓存AbstractAutowireCapableBeanFactory#populateBean
填充bean实例- 实例化Bean后置操作,如果显示实例化true,则直接返回 ❌
AutowiredAnnotationBeanPostProcessor#postProcessProperties
@autowired注解通过该方法对属性进行注入- 获取到依赖的属性a(转3),拿到代理bean / 其他的存的是原始bean
AbstractAutowireCapableBeanFactory#initializeBean
初始化bean,获得扩展的exposedObjectAbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
初始化之前的扩展处理AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
初始化之后的扩展处理- 没有扩展,所以bean==exposeObject
DefaultSingletonBeanRegistry#getSingleton(String,false)
从一级缓存和二级缓存中获取对象。此时两个缓存均没有,且不允许进入第三个缓存,所以earlySingletonReference
为空- 所以直接返回bean实例
- 将bean放入一级缓存
获取实例a
AbstractBeanFactory#getBean
->AbstractBeanFactory#doGetBean
获取属性依赖DefaultSingletonBeanRegistry#getSingleton(String)
singletonObjects
这个缓存中没存,但是bean已经在创建中了DefaultSingletonBeanRegistry#getSingleton(String,true)
singletonObjects
和earlySingletonObjects
这两个缓存中都没有,进入三级缓存:singletonFactory
AbstractAutowireCapableBeanFactory#getEarlyBeanReference
这个就是三级缓存AbstractAutoProxyCreator#wrapIfNecessary
生成代理 / 其他的没有这一步- 存入二级缓存(a, proxy)/ 其他的存的是原始bean
图片
问题原因
假如说是类a被代理,同时a引用b,b也引用a
对于正常的aop来说,会在b填充a的时候,a就已经是代理了
但是对于TEST的做法,它在b填充a的时候还是原来的bean,而在之后才代理,所以无法引用
解决方案
二方包:采用原生aop注解
用户侧:循环依赖的非代理bean,增加@lazy注解,不在容器刷新时加载,而是在使用时加载
原因是增加lazy注解后,121233处不会经过,121235处
earlySingletonReference
为空,直接返回exposedObject
的代理类当循环依赖属性真正被引用的时候,它会去加载之前的代理bean,完成循环依赖