参考文章:https://www.cnblogs.com/iceggboom/p/14393725.html
最近项目中使用@Async注解在方法上引起了循环依赖报错:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Bean with name 'classA' has been injected into other beans [classB] 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.@Async是什么
2.什么是循环依赖
3.为什么使用@async会引起循环依赖
4.如何解决
一、@Async
从Spring3开始提供了@Async注解,该注解可以被标注在方法上(也可以标注在类上,代表所有方法都异步调用),以便异步地调用该方法。调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行。
看定义很简单,其实就是新起一条线程执行注解下的方法,所以可以立即返回结果无需等待,常用于调用外部接口。
二、循环依赖
简单讲就是有A,B两个服务,A依赖B,B依赖A,这样就是一个死循环。如下图:
用代码表示:
@component
public class A{
@Autowired
private B b;
}
@component
public class B{
@Autowired
private A a;
}
三、为什么使用@async会引起循环依赖
其实当我们使用Spring的时候,默认是会解决循环依赖,但当使用了@Async注解方法后,处理循环依赖就失效了。
@Component
public class A{
@Autowired
private B b;
@Async
@Override
public void testA() {
}
}
@Component
public class ClassB{
@Autowired
private A a;
@Override
public void testB() {
a.testA();
}
}
如果要知道底层的前因后果,需要去分析源码,这里我用别人总结的话简单说一下:
1.context.getBean(A)开始创建A,A实例化完成后给A的依赖属性b开始赋值
2.context.getBean(B)开始创建B,B实例化完成后给B的依赖属性a开始赋值
3.重点:此时因为A支持循环依赖,所以会执行A的getEarlyBeanReference方法得到它的早期引用。而执行getEarlyBeanReference()的时候因为@Async根本还没执行,所以最终返回的仍旧是原始对象的地址
4.B完成初始化、完成属性的赋值,此时属性field持有的是Bean A原始类型的引用
5.完成了A的属性的赋值(此时已持有B的实例的引用),继续执行初始化方法initializeBean(...),在此处会解析@Aysnc注解,从而生成一个代理对象,所以最终exposedObject是一个代理对象(而非原始对象)最终加入到容器里
6.尴尬场面出现了:B引用的属性A是个原始对象,而此处准备return的实例A竟然是个代理对象,也就是说B引用的并非是最终对象(不是最终放进容器里的对象)
7.执行自检程序:由于allowRawInjectionDespiteWrapping默认值是false,表示不允许上面不一致的情况发生,so最终就抛错了
四、如何解决
1.使用@Lazy或者 @ComponentScan(lazyInit = true)
@Component
public class ClassB{
@Autowired
@Lazy
private A a;
@Override
public void testB() {
a.testA();
}
}
2.使用setter注入
@Component
public class ClassA{
private B b;
public void setB(B b) {
this.b = b;
}
@Async
@Override
public void testA() {
b.testb();
}
}
@Component
public class ClassB{
private A a;
public void setA(A a) {
this.a = a;
}
@Override
public void testB() {
a.testA();
}
}
3.使用@Autowired注解
@Component
public class A{
private B b;
@Autowired
public void SetB(B b) {
this.b= b;
}
@Async
@Override
public void testA() {
//TODO
}
}
4.重构方法(我是使用了这个方法)
重新建class,把@Async的方法放在新的类中,从根本上消除循环依赖