添加@EnableAsync注解后报循环依赖,注入失败咋办

简介: 在PayService类中注入了payNotifyService的实例,而在PayNotifyService类中又注入了payService的实例。而PayNotifyService类中又有一个加了@Async 注解的方法A。

情景再现

在PayService类中注入了payNotifyService的实例,而在PayNotifyService类中又注入了payService的实例。而PayNotifyService类中又有一个加了@Async 注解的方法A。

今天在公司项目中想将一个耗时的流程放在异步线程中执行,然后,按照操作手册,熟练了在异步方法上添加了@Async 注解,在对应模块的启动类中添加了@EnableAsync,妥妥的,准备跑一波看看效果。结果傻眼了。

报错啦!!!!!!


Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘payNotifyService’: Bean with name ‘payNotifyService’ has been injected into other beans [PayService] 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.


粗一看 ,这循环注入了嘛!奇怪了。没加@EnableAsync之前项目跑了好好的,加了这个注解之后就报错了,咋回事呢,小老弟。事不宜迟,google走一波。


原因分析

众所周知,在SpringBoot项目中添加@EnableAsync是使@Async 注解生效的。之前没加这个注解的时候异步方法都是没有生效的。而我们Async 是通过AOP生成代理类来实现异步执行的。所以

之前的情况是:

1.分别生成PayNotifyService类的实例A和PayService类的实例B,

2.通过@Autowired注解将实例注入到对应的类中。所以启动没问题。

现在的情况是:

前面两步的情况一样,但是当我们Spring的IOC容器检查到@Async 注解之后,会通过AOP这个方法所在的类生成一个代理类。注入的时候发现该Bean已经被其他对象注入了,所以这就出现了问题了。


解决办法

针对上面分析的原因,我们对应的有两种解决办法,

第一种方法

将@Autowired注解和@Lazy 搭配使用,使之注入的是代理类的Bean,而不是原始的Bean。

例如这样:

@Autowired
    @Lazy
    private payNotifyService payNotifyService;

第二种方法

将拥有@Async 注解的方法集中到一个类中,统一管理,也使得代码清晰易懂,便于维护,个人比较推荐这种方式。

第三种方法

使用基于 Setter 的注入,不通过@Autowired注解注入,而通过setter方法注入,这种方法,治标不治本,个人不建议。

扩展:异步调用获取返回结果:

如果我们想获取异步调用的返回结果?该如何处理呢?

答案就是在将方法的返回参数类型定义成Future<Object>,例如:

@Async
public Future<String> doTaskOne() throws Exception {
    System.out.println("开始做任务一");
    long start = System.currentTimeMillis();
    Thread.sleep(random.nextInt(10000));
    long end = System.currentTimeMillis();
    System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
    return new AsyncResult<>("任务一完成");
}

如上,是一个通过@Async修饰的异步方法。我们将真正需要的结果封装在Futrue中,实际返回时我们只需要通过new AsyncResult<>(”任务一完成“)。

外部方法调用该异步方法,获取返回结果只需要如下:

doTaskOne().get()
相关文章
|
2月前
|
Java
SpringBoot 内部方法调用,事务不起作用的原因及解决办法
在做业务开发时,遇到了一个事务不起作用的问题。大概流程是这样的,方法内部的定时任务调用了一个带事务的方法,失败后事务没有回滚。查阅资料后,问题得到解决,记录下来分享给大家。
143 4
|
7月前
启动俩万报错异常处理
启动俩万报错异常处理
37 2
|
6月前
|
Java Spring
Spring循环依赖问题之构造器内的循环依赖如何解决
Spring循环依赖问题之构造器内的循环依赖如何解决
|
6月前
|
XML Java 数据格式
循环依赖问题之创建Bean的过程中发生异常,Spring会如何处理
循环依赖问题之创建Bean的过程中发生异常,Spring会如何处理
|
6月前
|
存储 缓存 Java
Spring循环依赖问题之循环依赖异常如何解决
Spring循环依赖问题之循环依赖异常如何解决
测试时,请求方法一定要写对,写错照样出问题,Method Not Allowed 删除接口写错,注意Controller层中UserMapper中的写法,视频往后看看就能看到解决问题的方法了
测试时,请求方法一定要写对,写错照样出问题,Method Not Allowed 删除接口写错,注意Controller层中UserMapper中的写法,视频往后看看就能看到解决问题的方法了
|
8月前
|
存储 缓存 Java
ioc循环依赖怎么解决
ioc循环依赖怎么解决
80 0
|
Android开发
Bean莫名其妙的报错还可以运行。
cannot resolve 一大堆方法名。很奇怪,这其实并不会影响正常运行,值是在.get方法名的时候 get不到,看着也十分别扭。
109 0
|
Java Spring
一不小心,你就掉进了Spring延迟初始化的坑!
由于 spring bean 的默认作用域是:singleton。所以在启动的时候 bean 会被初始化,如果被标记了@Lazy,会延迟初始化,但是如果被非懒加载的 Bean 注入了,@Lazy会失效。
|
存储 缓存 安全
【Spring源码】循环依赖如何处理?
面试官:“看过Spring源码吧,简单说说Spring如何解决循环依赖问题?” 大神仙:“Spring利用到了三级缓存来解决循环依赖问题”。 面试官:“三级缓存是怎么处理的?为什么一定得是三级缓存?三级缓存别是对应存储的是什么?” 大神仙:“......”
187 0
【Spring源码】循环依赖如何处理?