上一篇文章spring循环依赖下使用@Async注解导致报错问题提出了@Async导致spring循环依赖失效的问题,今天这篇文章就重点分析原因;里面涉及到spring的源码相关的知识,比较复杂;
案例:
先上测试代码:
1.创建schoolService,注入studentService;
@Service
public class SchoolService {
@Autowired
private StudentService studentService;
@Async
public void updateSchool(){
...
}
}
2.创建studentService,注入schoolService
@Service
public class StudentService {
@Autowired
private SchoolService schoolService;
}
3.启动服务,前提是spring先加载的schoolService,就会报错如下:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'schoolService': Bean with name 'schoolService' has been injected into other beans [studentService] in its raw version as part of a circular reference, but has eventually been wrapped. 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 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
分析原因:
1.定位到具体代码:
AbstractAutowireCapableBeanFactory类的doCreateBean方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
//创建对象,利用反射new一个具体对象
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
//earlySingletonExposure为true isSingleton是否单例,为true;allowCircularReferences是否允许循环依赖,默认就为true;isSingletonCurrentlyInCreation为true
boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
this.addSingletonFactory(beanName, () -> {
return this.getEarlyBeanReference(beanName, mbd, bean);
});
}
Object exposedObject = bean;
try {
//对schoolService进行属性注入,包括注入StudentService
this.populateBean(beanName, mbd, instanceWrapper);
//对schoolService进行初始化,这个里面会调用@Async这个注解对应的处理器,生成一个schoolService动态代理对象exposedObject
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
......
}
//earlySingletonExposure为true
if (earlySingletonExposure) {
//从二级缓存earlySingletonObjects中获取到schoolService的对象,还不是bean;
Object earlySingletonReference = this.getSingleton(beanName, false);
if (earlySingletonReference != null) {
//由于exposedObject是经过动态代理的,所以和bean不同
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
} else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {
//getDependentBeans就是获取自己依赖的对象,是studentService;
String[] dependentBeans = this.getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);
String[] var12 = dependentBeans;
int var13 = dependentBeans.length;
for(int var14 = 0; var14 < var13; ++var14) {
String dependentBean = var12[var14];
if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
//由于依循环赖的studentService已经创建好了,所以会被添加进去
actualDependentBeans.add(dependentBean);
}
}
//actualDependentBeans不为空,报错
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been wrapped. 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 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
try {
this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
} catch (BeanDefinitionValidationException var16) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
}
}
2.分析:循环依赖的流程图如下图所示:
1.首先加载schoolService,尝试从一级缓存获取,没有;
2.检查是作用域是单例isSingleton,将bean标记为正在创建,
2.1调用createBean--》doCreateBea方法开始创建bean;
2.2 doCreateBea里面先利用反射创建SchoolService普通对象,然后将其放入三级缓存singletonFactories;beanName为key,value是lambda表达式,里面会判断是否执行处理器,进行AOP动态代理;
2.3 执行populateBean方法属性注入studentService;但是我们的studentService还没有,所以就会创建studentService;
2.3.1 创建studentService;按照上面的顺序-----》执行到populateBean的时候,studentService就会注入schoolService;
2.3.2 调用getSingleton方法,获取到schoolService的早期对象;
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//尝试从一级缓存/单例池获取,没有,因为schoolService还只是一个对象,不是bean
Object singletonObject = this.singletonObjects.get(beanName);
//schoolService在首次创建的时候是被标记为正在创建,所以isSingletonCurrentlyInCreation为true
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
synchronized(this.singletonObjects) {
//从二级缓存获取,还是没有,
singletonObject = this.earlySingletonObjects.get(beanName);
//allowEarlyReference为true,
if (singletonObject == null && allowEarlyReference) {
//从三级缓存获取到,并执行lambda表达式获取到schoolService对象,并将该对象放入二级缓存,从三级缓存删除,避免重复创建
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
2.3.4 studentService成功获取到schoolService的早期对象,完成了属性注入,然后执行初始化方法完成 studentService的bean创建;创建完成返回到如下代码
上面的代码成功创建了studentService,并且将其放入单例池完成bean的创建;
我们回到schoolService的populatBean方法那里去;
2.4 执行schoolService的初始化方法;在这个方法里面,会执行async的处理器,AsyncAnnotationBeanPostProcessor 它是一个 BeanPostProcessor,实现了 postProcessAfterInitialization 方法,也就是在初始化后期执行;这里为schoolService生成一个代理对象;
2.5 初始化执行完毕:执行下面的判断代码会报错;
//earlySingletonExposure为true
if (earlySingletonExposure) {
//getSingleton方法从二级缓存获取schoolService的早期对象
Object earlySingletonReference = this.getSingleton(beanName, false);
if (earlySingletonReference != null) {
//exposedObject是初始化后的,由于@Async生成了一个代理对象,所以下面的判断为false
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
//下面的判断,hasDependentBean里是判断schoolService的循环依赖的bean有没有,schoolService和studentService是循环依赖,所以为true
} else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {
//获取schoolService的循环依赖studentService
String[] dependentBeans = this.getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);
String[] var12 = dependentBeans;
int var13 = dependentBeans.length;
for(int var14 = 0; var14 < var13; ++var14) {
String dependentBean = var12[var14];
//removeSingletonIfCreatedForTypeCheckOnly就是判断schoolService循环依赖的对象studentService是不是已经创建成功了,如果创建成功了,肯定就不合理了
if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
//放入actualDependentBeans;
actualDependentBeans.add(dependentBean);
}
}
//肯定就不为空了,所以就报错了
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been wrapped. 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 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
总结:
由于循环依赖,如果一个bean提供给其它bean依赖的对象和自己本身的对象不同,肯定就不合理了,比如schoolService给循环依赖studentService的属性是注入是一个普通对象,但是给其它bean,不是循环依赖的是动态代理的对象,这样如果我在studentService里面调用schoolService的异步方法,由于不是动态代理的对象,所以该方法也就不会是异步的,但是其它不是循环依赖的bean对象里面调用schoolService的异步方法又是异步;这样就不合理了;