Spring:解决因@Async引起的循环依赖报错
最近项目中使用@Async注解在方法上引起了循环依赖报错:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Bean with name 'classA' has been injected into other beans [classB] 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.
先提几个问题:
1.@Async是什么
2.什么是循环依赖
3.为什么使用@async会引起循环依赖
4.如何解决
一、@Async
从Spring3开始提供了@Async注解,该注解可以被标注在方法上(也可以标注在类上,代表所有方法都异步调用),以便异步地调用该方法。调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行。
看定义很简单,其实就是新起一条线程执行注解下的方法,所以可以立即返回结果无需等待,常用于调用外部接口。
二、循环依赖
简单讲就是有A,B两个服务,A依赖B,B依赖A,这样就是一个死循环。如下图:
用代码表示:
@component
public class A{
@Autowired
private B b;
} @component
public class B{
@Autowired
private A a;
}
三、为什么使用@async会引起循环依赖
其实当我们使用Spring的时候,默认是会解决循环依赖,但当使用了@Async注解方法后,处理循环依赖就失效了。
@Component
public class A{
@Autowired
private B b; @Async
@Override
public void testA() { }
}
@Component
public class ClassB{
@Autowired
private A a; @Override
public void testB() {
a.testA();
}
}
如果要知道底层的前因后果,需要去分析源码,这里我用别人总结的话简单说一下:
1.context.getBean(A)开始创建A,A实例化完成后给A的依赖属性b开始赋值
2.context.getBean(B)开始创建B,B实例化完成后给B的依赖属性a开始赋值
3.重点:此时因为A支持循环依赖,所以会执行A的getEarlyBeanReference方法得到它的早期引用。而执行getEarlyBeanReference()的时候因为@Async根本还没执行,所以最终返回的仍旧是原始对象的地址
4.B完成初始化、完成属性的赋值,此时属性field持有的是Bean A原始类型的引用
5.完成了A的属性的赋值(此时已持有B的实例的引用),继续执行初始化方法initializeBean(...),在此处会解析@Aysnc注解,从而生成一个代理对象,所以最终exposedObject是一个代理对象(而非原始对象)最终加入到容器里
6.尴尬场面出现了:B引用的属性A是个原始对象,而此处准备return的实例A竟然是个代理对象,也就是说B引用的并非是最终对象(不是最终放进容器里的对象)
7.执行自检程序:由于allowRawInjectionDespiteWrapping默认值是false,表示不允许上面不一致的情况发生,so最终就抛错了
四、如何解决
1.使用@Lazy或者 @ComponentScan(lazyInit = true)
@Component
public class ClassB{ @Autowired
@Lazy
private A a; @Override
public void testB() {
a.testA();
}
}
2.使用setter注入
@Component
public class ClassA{ private B b; public void setB(B b) {
this.b = b;
}
@Async
@Override
public void testA() {
b.testb();
}
}
@Component
public class ClassB{ private A a; public void setA(A a) {
this.a = a;
} @Override
public void testB() {
a.testA();
}
}
3.使用@Autowired注解
@Component
public class A{ private B b; @Autowired
public void SetB(B b) {
this.b= b;
} @Async
@Override
public void testA() {
//TODO
}
}
4.重构方法
重新建class,把@Async的方法放在新的类中,从根本上消除循环依赖
Spring:解决因@Async引起的循环依赖报错的更多相关文章
- Spring中解决循环依赖报错的问题
什么是循环依赖 当一个ClassA依赖于ClassB,然后ClassB又反过来依赖ClassA,这就形成了一个循环依赖: ClassA -> ClassB -> ClassA 原创声明 本 ...
- Spirng 循环依赖报错:Requested bean is currently in creation: Is there an unresolvable circular reference?
1:前言 最近在项目中遇到了一次循环依赖报错的问题,虽然解决的很快,但是有些不明白的地方,特此记录. 在此我把 bean 的结构和 注入方式单独拎出来进行演示 1.1:报错提示 1.2:错误日志 Ex ...
- IDEA循环依赖报错解决方案
step1.查找循环依赖 step2.在IDEA菜单栏中打开Analyze->Analyze Module Dependencies...看到有的模块被红色的标出来了,此时右边显示了循环依赖,那 ...
- Spring IOC 容器源码分析 - 循环依赖的解决办法
1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...
- spring jpa 实体互相引用返回restful数据循环引用报错的问题
spring jpa 实体互相引用返回restful数据循环引用报错的问题 Java实体里两个对象有关联关系,互相引用,比如,在一对多的关联关系里 Problem对象,引用了标签列表ProblemLa ...
- AS添加依赖报错Unable to merge dex
AS添加依赖报错Unable to merge dex 最近在给项目添加依赖的时候,要给项目导入Bmob的SDK,参照Bmob的官方文档,可以直接在app的build.gradle文件中添加 //Bm ...
- spring JMS在接收消息的时候总是报错
spring JMS在接收消息的时候总是报错 org.springframework.jms.UncategorizedJmsException: Uncategorized exception oc ...
- Maven引入Hadoop依赖报错:Missing artifact jdk.tools:jdk.tools:jar:1.6
Maven引入Hadoop依赖报错:Missing artifact jdk.tools:jdk.tools:jar:1.6 原因是缺少tools.jar的依赖,tools.jar在jdk的安装目录中 ...
- 解决windows64位系统上安装mysql-python报错
解决windows64位系统上安装mysql-python报错 2018年03月12日 13:08:24 一个CD包 阅读数:1231 版权声明:本文为博主原创文章,未经博主允许不得转载. ht ...
随机推荐
- rac双节点+物理DG
注:以下文章均是看了黄伟老师的视频,记录为博客供以后使用. 双节点RAC搭建: http://blog.csdn.net/imliuqun123/article/details/76171289 RA ...
- Oracle 索引原理分析
索引是一种允许直接访问数据表中某一数据行的树型结构,为了提高查询效率而引入,是一个独立于表的对象,可以存放在与表不同的表空间中.索引记录中存有索引关键字和指向表中数据的指针(地址).对索引进行的I/O ...
- 【Oracle】密码文件相关
Oracle数据库的orapwd命令,主要用来建立密码(口令)文件. 一.查看帮助信息 [oracle@oracle11g dbs]$ orapwd Usage: orapwd file=<fn ...
- C# datagridview设置标题为汉语
正常情况下,在给datagridview绑定数据源之后,显示的是SQL语句中的栏位,如下 我们想让标题显示汉语,可以有一下两种方法 1.在SQL中设置列别名 SELECT TITLE AS '报警标题 ...
- perl打开本地/服务器图片
index.html <html> <body> <h2> perl read img </h2> <img src = "displa ...
- Devexpress DockManager多页面浮动窗口会关闭所有页面的问题
注册 DockManager 的 ClosingPanel 事件 private void DockManager1_ClosingPanel(object sender, DockPanelCanc ...
- 封装JSONP 函数,方便请求发送
封装JSONP 函数,方便请求发送 封装jsonp的代码和封装Ajax的代码非常的相似!可以参照食用偶! <button id="btn">点击我发送请求!</b ...
- OAuth2.0是干什么的?
OAuth2.0是干什么的? 首先用户有一些数据: 将数据存储在服务器上: 这时候有一个应用要访问数据: 如果这个应用是一个恶意程序呢?所以需要一个检验来判断请求是不是安全的: 如何判断是不是安全的? ...
- Manachar’s Algorithm
Manachar's Algorithm Longest palindromic substring - Wikipedia https://en.wikipedia.org/wiki/Longes ...
- The Go Blog Getting to Go: The Journey of Go's Garbage Collector
Getting to Go: The Journey of Go's Garbage Collector https://blog.golang.org/ismmkeynote