前言

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

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐