文章目录
前言
发现不少网友存在使用@Async导致的循环依赖。在此提供一个解决办法。
一、@Async 循环依赖起因
起因:bean相互依赖时,其一bean使用了@Async
1、即存在aService和bService两个bean,且两个bean都相互依赖引用了;
2、然后aService的某方法需要异步执行,且加了@Async注解(例如update方法);
3、报错。。。
详细问题如下
由@Async引起的Spring循环引用
类a
类b
二、基本解决方案
常用解决方案,如下
使用@Async异步注解导致该Bean在循环依赖时启动报BeanCurrentlyInCreationException异常的根本原因分析,以及提供解决方案【享学Spring】:
文中方案如下:
三、新方案——升级上文中的方案3
假设原方法如下
private static void doSomeThing() {
User user = new User(1, "张三");
aService.update(user);
//或
aService.doInit();
}
换成异步
简而言之,定义一个异步工具类AsyncUtil,对想要异步的方法进行包装即可。
步骤一、首先,定义一个异步工具类
//定义异步工具类,用于执行异步任务
@Component
public class AsyncUtils{
@Async
public void exec(Object param, Consumer consumer){
consumer.accept(param);
}
}
步骤二、在需要异步执行方法的bean(例如aService),注入该工具类
public class aService{
//注入异步工具类
@Autowired
public AsyncUtils asyncUtils;
...
}
步骤三、使用asyncUtils.exec来执行方法
使用方式1(有参):比如类bService要异步执行aService的update(Object obj)方法,可以通过下面的方法进行异步执行。
//执行异步任务表
private static void doSomeThing() {
//1、参数
User user = new User(1, "张三");
//2、定义Consumer任务
Consumer asyncTask = param -> aService.update(param);
//3、执行asyncTask方法,相当于执行 aService.update(obj);
asyncUtils.exec(user , asyncTask);
}
使用方式2(无参):若aService.update()是无参的,则还省了一步操作,如下
//执行异步任务表
private static void doSomeThing() {
//1、定义Consumer任务
Consumer asyncTask = param -> aService.doInit();
//2、执行asyncTask方法,因为不用传参,所以参数传个null就行
asyncUtils.exec(null, asyncTask);
}
使用方式3(有参):方式1的简写,一行即可。
//执行异步任务表
private static void doSomeThing() {
asyncUtils.exec(new User(1, "张三"), param -> aService.update(param));
}
使用方式4(无参):方式2的简写,一行即可。
//执行异步任务表
private static void doSomeThing() {
asyncUtils.exec(null, param -> aService.doInit(););
}
(注意:记住param是形参,换成其他单词或字母都行,无关紧要)
四、验证-打日志,看线程名
提示:想知道是否异步执行成功,可以打个log看看线程名称,在不同线程打的日志显示不同的名称,如下图中【cTaskExecutor-1】和【restartedMain】两个线程打的日志。
五、扩展(看个人需要吧)
场景概述:类内调用的方法需要异步执行,且不想引入其他工具类。
例如:假设该类为HttpClientUtil,方法odPost()方法需要异步调用post()方法,则可以通过下述方法进行执行。
解决:使用将异步工具类设在类内部(内部类)进行使用。
步骤一、该类加上@Component,变成bean。
步骤二、定义内部工具类
步骤三、注入内部的异步工具类bean
步骤四、异步调用
//步骤一、变成bean
@Component
public class HttpClientUtil {
//......
public static HttpClientUtil.AsyncUtils asyncUtils;
//步骤三、注入内部的异步工具类bean
@Autowired
public void setAsyncUtils(HttpClientUtil.AsyncUtils asyncUtils){
this.asyncUtils = asyncUtils;
}
//步骤二、定义内部异步工具类,用于执行异步任务
@Component
public class AsyncUtils{
@Async
public void exec(Object param, Consumer consumer){
consumer.accept(param);
}
}
//步骤四、调用,调用方法同上文一致,这里就不追杀
public static String doPost(){
//业务代码......
//1、将post()方法定义为Consumer任务
Consumer asyncTask = param -> post();
//2、执行asyncTask方法,因为不用传参,所以参数传个null就行
asyncUtils.exec(null, asyncTask);
//后续业务代码......
}
public static String post(){
//do some thing
}
}
参考博文如下:
1、由@Async引起的Spring循环引用:https://blog.csdn.net/qq_42559485/article/details/115749978
2、使用@Async异步注解导致该Bean在循环依赖时启动报BeanCurrentlyInCreationException异常的根本原因分析,以及提供解决方案【享学Spring】:https://fangshixiang.blog.csdn.net/article/details/92797058
3、关于对spring注入bean的顺序,以及spring如何保证事先加载依赖bean的问题:https://wenku.baidu.com/view/59309afdcd2f0066f5335a8102d276a200296006.html