解决spring使用@Async 导致的循环依赖报错


前言

发现不少网友存在使用@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

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
回答: 当使用@Async注解时,可能会出现报错的情况。根据引用\[1\]中的内容,有几个常见的原因可能导致报错。首先,被注解@Async的方法不能是public方法。其次,被注解@Async的方法的返回值只能是void或者Future。另外,如果被注解@Async的方法使用了static修饰,也会失效。此外,如果没有在类上加上注解@Async或@EnableAsyncSpring无法扫描到异步类,也会导致报错。还有一点需要注意的是,调用方与被调方不能在同一个类,因为Spring在扫描bean时会生成代理类来调用有注解的方法,如果调用方与被调方在同一个类,调用将直接通过原来的bean,而不是通过代理类,导致@Async失效。因此,一般将要异步执行的方法单独抽取成一个类。最后,如果在Async方法上标注@Transactional是无效的,但在Async方法调用的方法上标注@Transactional是有效的。希望这些信息能帮助你解决报错问题。 #### 引用[.reference_title] - *1* [@Async失效情况](https://blog.csdn.net/ElendaLee/article/details/127411225)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [async/await如何处理错误](https://blog.csdn.net/O_x_y_g_e_n/article/details/123079080)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值