阿里云开发者社区,千万开发者的选择
阿里云开发者社区,百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,欢迎点击【阅读原文】加入我们。
阿里妹导读
本文主要梳理了Spring解决bean循环依赖的思路。
一、背景
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'xxxManageFacadeImpl': Bean with name 'xxxManageFacadeImpl' has been injected into other beans [xxxProductMaintenceFacadeImpl]
in its raw version as part of a circular reference, but has eventually been wrap means thff, for expped. This means that said other beans do not use the final version of the bean.
This is often the result of over-eager type matching - consider using 'getBeanNamesallowEageOfType' with the 'allowEagerInit' flag turned off, for example
二、相关知识点简介
2.1、什么是Bean循环依赖?
2.2、Spring创建Bean主要流程
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// Bean初始化第一步:默认调用无参构造实例化Bean
// 如果是只有带参数的构造方法,构造方法里的参数依赖注入,就是发生在这一步
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// bean创建第二步:填充属性(DI依赖注入发生在此步骤)
populateBean(beanName, mbd, instanceWrapper);
// bean创建第三步:调用初始化方法,完成bean的初始化操作(AOP的第三个入口)
// AOP是通过自动代理创建器AbstractAutoProxyCreator的postProcessAfterInitialization()
//方法的执行进行代理对象的创建的,AbstractAutoProxyCreator是BeanPostProcessor接口的实现
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
// ...
}
// ...
1. 实例化Bean
主要是通过反射调用默认构造函数创建Bean实例,此时bean的属性都还是默认值null。被注解@Bean标注的方法就是此阶段被调用的。
2. 填充Bean属性
这一步主要是对bean的依赖属性进行填充,对@Value @Autowired @Resource注解标注的属性注入对象引用。
3. 调用Bean初始化方法
调用配置指定中的init 方法,如xml文件指定bean的init-method方法或注解@Bean(initMethod = "initMethod")指定的方法。
2.3、Bean创建过程BeanPostProcessor接口拓展点
postProcessMergedBeanDefinition:可对BeanDefinition添加额外的自定义配置
getEarlyBeanReference:返回早期暴露的bean引用,一个典型的例子是循环依赖时如果有动态代理,需要在此先返回代理实例
postProcessAfterInstantiation:在populateBean前用户可以手动注入一些属性
postProcessProperties:对属性进行注入,例如配置文件加密信息在此解密后注入
postProcessBeforeInitialization:属性注入后的一些额外操作
三、Spring如何解决循环依赖?
3.1、三级缓存作用
3.2、三级缓存解决循环依赖过程
// AbstractBeanFactory.java
protected <T> T doGetBean(final String name, final Class<T> requiredType,
Object[] args, boolean typeCheckOnly) throws BeansException { final
final String beanName = transformedBeanName(name);
// 1.尝试从缓存中获取bean,AService还没创建三级缓存都没命中
Object sharedInstance = getSingleton(beanName);
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> { //注意此处参数是一个lambda表达式即参数传入的是ObjectFactory类型一个匿名内部类对象
try {
return createBean(beanName, mbd, args); //
}
catch (BeansException ex) {}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
//将当前beanName放到singletonsCurrentlyInCreation 集合中,标识该bean正在创建
beforeSingletonCreation(beanName);
//通过回调getObject()方法触发AbstractAutowireCapableBeanFactory#createBean(String beanName, RootBeanDefinition mbd, Object[] args)的执行
singletonObject = singletonFactory.getObject();
afterSingletonCreation(beanName);
addSingleton(beanName, singletonObject);
}
将当前beanName放到singletonsCurrentlyInCreation集合中标识该bean正在创建;
调用匿名内部类实例对象的getObject()方法触发AbstractAutowireCapableBeanFactory#createBean方法的执行;
将当前beanName从singletonsCurrentlyInCreation集合中移除;
//真正创建Bean的地方 AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// bean初始化第一步:默认调用无参构造实例化Bean
// 构造参数依赖注入,就是发生在这一步
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 实例化后的Bean对象
final Object bean = instanceWrapper.getWrappedInstance();
// 将刚创建的bean放入三级缓存中singleFactories(key是beanName,value是ObjectFactory)
//注意此处参数又是一个lambda表达式即参数传入的是ObjectFactory类型一个匿名内部类对象,在后续再缓存中查找Bean时会触发匿名内部类getEarlyBeanReference()方法回调
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// Initialize the bean instance.
Object exposedObject = bean;
try {
// bean创建第二步:填充属性(DI依赖注入发生在此步骤)
populateBean(beanName, mbd, instanceWrapper);
// bean创建第三步:调用初始化方法,完成bean的初始化操作(AOP的第三个入口)
// AOP是通过自动代理创建器AbstractAutoProxyCreator的postProcessAfterInitialization()
//方法的执行进行代理对象的创建的,AbstractAutoProxyCreator是BeanPostProcessor接口的实现
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
// ...
}
}
//DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存获取,key=AService
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从二级缓存获取,key=AService
singletonObject = this.earlySingletonObjects.get(beanName);
// 是否允许循环引用
if (singletonObject == null && allowEarlyReference) {
// 前面已经将以Key为AService,value是ObjectFactory类型一个匿名内部类对象放入三级缓存了
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//singletonFactory是一个匿名内部类对象,此处触发匿名内部类中getEarlyBeanReference()方法回调。
singletonObject = singletonFactory.getObject();
// 将三级缓存生产的bean放入二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 删除三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
3.3、 当AOP遇到循环依赖
// 将Aservice添加三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 添加Bservice的aService属性时从三级中找Aservice的ObjectFactory类型一个匿名内部类对象,从而触发匿名内部类getEarlyBeanReference()方法回调,进入创建AService切面代理对象逻辑
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
//判断后置处理器是否实现了SmartInstantiationAwareBeanPostProcessor接口
//调用SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
//真正实现了该方法的类就是AbstractAutoProxyCreator
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
// 先获取beanName,主要是为FactoryBean类型添加&前缀
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 判断是否已经在earlyProxyReferences集合中,不在则添加进去
if (!this.earlyProxyReferences.contains(cacheKey)) {
this.earlyProxyReferences.add(cacheKey);
}
// 创建代理对象,如果必要的话
return wrapIfNecessary(bean, beanName, cacheKey);
}
/**
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 前面先做一些基本的判断
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// Advice/Pointcut/Advisor/AopInfrastructureBean接口的beanClass不进行代理以及对beanName为aop内的切面名也不进行代理
// 此处可查看子类复写的shouldSkip()方法
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 查找对代理类相关的advisor对象集合,此处就与point-cut表达式有关了
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 对相应的advisor不为空才采取代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 通过jdk动态代理或者cglib动态代理,产生代理对象,这里传入的是SingletonTargetSource对象喔,对原始bean对象进行了包装
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// 放入代理类型缓存
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
// 放入通知缓存
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
}
四、为啥我们应用还会报错
五、总结
阿里云开发者社区,千万开发者的选择
阿里云开发者社区,百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,欢迎点击【阅读原文】加入我们。