Spring:解决因@Async引起的循环依赖报错
最近项目中使用@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的方法放在新的类中,从根本上消除循环依赖