使用@Async异步注解导致该Bean在循环依赖时启动报BeanCurrentlyInCreationException异常的根本原因分析,以及提供解决方案
今天在自己项目中使用@Async
的时候,碰到了一个问题:Spring循环依赖(circular reference)问题
。
或许刚说到这,有的小伙伴就会大惊失色了。Spring不是解决了循环依赖问题吗,它是支持循环依赖的呀?怎么会呢?
出现使用@Async
导致循环依赖问题的必要
条件:
- 已开启
@EnableAsync
的支持 @Async
注解所在的Bean被循环依赖了
为什么有小伙伴跟我说:我使用@Async即使本类方法调用也从来木有遇到这个错误啊?难道它不常见?
为此经过我的一番调查,包括看一些同事、小伙伴的代码发现:并不是使用@Async没有启动报错,而是他本类调用的时候直接调用的方法,这样@Async是不生效的但小伙伴却全然不知而已。
至于@Async没生效这种问题为何没报出来???甚至过了很久很久都没人发现和关注??
其实道理很简单,它和事务不生效不一样,@Async若没生效99%情况下都不会影响到业务的正常进行,因为它不会影响数据正确性,只会影响到性能(无非就是异步变同步呗,这是兼容的)
我们知道事务不生效和@Async不生效的根本原因都是同一个:直接调用了本类方法而非接口方法/代理对象方法。
解决这类不生效问题的方案一般我们都有两种:
自己注入自己,然后再调用接口方法(当然此处的一个变种是使用编程方式形如:AInterface a = applicationContext.getBean(AInterface.class);这样子手动获取也是可行的~~~本文不讨论这种比较直接简单的方式)
使用AopContext.currentProxy();
让不调用本类的@Async
方法不就可以了;让不产生循环依赖不就可以了,这也是解决方案
1:新建一个线程池配置类AsyncConfig
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); //核心线程数
executor.setMaxPoolSize(20); //最大线程数
executor.setQueueCapacity(1000); //队列大小
executor.setKeepAliveSeconds(300); //线程最大空闲时间
executor.setThreadNamePrefix("fsx-Executor-"); //指定用于新创建的线程名称的前缀。
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略(一共四种,此处省略)
executor.initialize();
return executor;
}
// 异常处理器:当然你也可以自定义的,这里我就这么简单写了~~~
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
2:新增一个HelloService接口
public interface HelloService {
Object hello(Integer id);
void fun1();
}
3:新增一个HelloServiceImpl实现类
@Service
public class HelloServiceImpl implements HelloService{
@Autowired
private HelloService helloService;
@Override
public Object hello(Integer id) {
System.out.println("线程名称:" + Thread.currentThread().getName());
helloService.fun1(); // 使用接口方式调用,而不是this
return "service hello";
}
@Async
@Override
public void fun1() {
System.out.println("线程名称:" + Thread.currentThread().getName());
}
}
4:新增一个异步测试类
@SpringBootTest
public class AsyncTest {
@Autowired
private HelloService helloService;
@Test
public void test(){
helloService.hello(1);
}
}
5:启动测试类
此种做法首先是Spring中一个典型的循环依赖场景:自己依赖自己
。本以为能够像解决事务不生效问题一样依旧屡试不爽
,但没想到非常的不给面子,启动即报错
报错如下BeanCurrentlyInCreationException异常
6:解决方法
在HelloServiceImpl类上@Autowired注解下加上@Lazy注解,即懒加载
7:启动项目
输出如下,成功了