Spring 事务、异步和循环依赖有什么关系?
前言
在循环依赖中有一种循环依赖,就是自注入:自己依赖自己。

事务的自注入
在 Spring 自调用事务失效,你是怎么解决的? 有小伙伴提出可以自己注入自己来解决事务失效。
具体使用方式如下:
@Slf4j
@Service
public class OrderBizServiceImpl implements OrderBizService {
// 注入自己
@Autowired
private OrderBizService orderBizService;
@Override
public void callBack() throws Exception {
// 一系列的逻辑
// 需要事务操作更新订单和用户金额
orderBizService.updateOrderStatusAndUserBalance();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateOrderStatusAndUserBalance() throws Exception {
// 内部是事务逻辑
}
}
是不是发现很神奇的事情,事务生效了。
其实这里注入自己,其实是注入的一个代理对象,调事务,也是调的代理对象的事务,所以事务生效。
Spring 事务失效原因:
事务只能应用到 public 方法上才会有效;
事务需要从外部调用,Spring 自调用会失效;
建议事务注解 @Transactional 一般添加在实现类上。
异步的自注入
发现 @Transactional 注解可以自注入解决事务失效的问题,在某次开发中,自然而然想到 @Async 异步是不是也可以自注入解决循环依赖的问题。
NO, NO, NO……
事实告诉我们是不可以的!

从错误开始着手:

开始往上面反推 exposedObject == bean 是这一块出了问题。
也就是说异步的时候,再次从二级缓存中获取的和初始的不相同。
Object earlySingletonReference = getSingleton(beanName, false);

这一次获取的时候发现不同所以报错。
那就开始 Debug, 按照循环依赖的逻辑,执行到 populateBean 时,属性赋值,发现有依赖自己,此时会创建自己。
执行 singleton.getObject 方法


而此时执行 getEarlyBeanReference 先判断 InfrastructureAdvisorAutoProxyCreator true 调用 wrapIfNecessary 判断是否生成一个代理对象,这里并没有生成代理对象。

然后开始执行异步的 AsyncAnnotationBeanPostProcessor 判断为 false。所以没有执行异步的生成代理对象逻辑。
那就继续往下看

进入到 initializeBean 的逻辑,有一部分叫做 applyBeanPostProcessorsAfterInitialization
方面小伙伴搜索,所以贴出来代码关键字。IDEA 使用
⌘ + Shift + F搜索。

循环执行后置处理器:


发现执行完 AsyncAnnotationBeanPostProcessor 这个 PostProcessor 后,对象被改变了。从而导致二级缓存和当前的 Bean 不同。
以上也就是为什么 @Async 自调用不可以,因为在后面初始化阶段被代理修改了对象。
@Transactional 为什么可以呢?


先判断 InfrastructureAdvisorAutoProxyCreator true 生成一个代理对象。

事务的处理器 PersistenceExceptionTranslationPostProcessor 也没有执行。
继续 Debug 关注 applyBeanPostProcessorsAfterInitialization

执行结束,发现 Bean 没有发生改变。
总结
- @Transactional: 是在循环依赖从二级缓存升到三级缓存的时候已经生成了代理对象。
- @Async: 是在初始化阶段(initializeBean)去生成代理对象。然后 @Async 导致后面判断
exposedObject == bean为 false ,从而抛出异常。

可以看出图中有两处会执行 BeanPostProcessor :
- 在 singletonFactory.getObject 时,如果是 SmartInstantiationAwareBeanPostProcessor 的子类会执行 getEarlyBeanReference 方法。
- 在 initializeBean 的 applyBeanPostProcessorsAfterInitialization 时会执行所有 BeanPostProcessor 的 postProcessAfterInitialization 的方法。
也有其他的地方在执行后置处理器,比如 applyBeanPostProcessorsBeforeInitialization ,只不过这里关注这俩处。
而这两处都有可能生成代理对象, @Transactional 是在 getEarlyBeanReference 处生成的代理对象,所以后面判断 Bean 是否被改变时为 true,而 @Async 是在后面异步生成了代理对象,所以判断不通过。
至此,分析完毕,错误之处,欢迎指正。
相关推荐
Spring 事务、异步和循环依赖有什么关系?的更多相关文章
- Spring 是如何解决循环依赖的?
前言 相信很多小伙伴在工作中都会遇到循环依赖,不过大多数它是这样显示的: 还会提示这么一句: Requested bean is currently in creation: Is there an ...
- [跟我学spring学习笔记][DI循环依赖]
循环依赖 什么是循环依赖? 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方. Spring容器循环依赖包括构造器循环依赖和setter循环依赖,那Spring容器如何解决循环依赖呢? ...
- Spring.getBean()流程和循环依赖的解决
getBean流程介绍(以单例的Bean流程为准) getBean(beanName) 从BeanFactory中获取Bean的实例对象,真正获取的逻辑由doGetBean实现. doGetBean( ...
- Spring是如何解决循环依赖的
前言 在面试的时候这两年有一个非常高频的关于spring的问题,那就是spring是如何解决循环依赖的.这个问题听着就是轻描淡写的一句话,其实考察的内容还是非常多的,主要还是考察的应聘者有没有研究过s ...
- spring: 我是如何解决循环依赖的?
1.由同事抛的一个问题开始 最近项目组的一个同事遇到了一个问题,问我的意见,一下子引起的我的兴趣,因为这个问题我也是第一次遇到.平时自认为对spring循环依赖问题还是比较了解的,直到遇到这个和后面的 ...
- 听说你还不知道Spring是如何解决循环依赖问题的?
Spring如何解决的循环依赖,是近两年流行起来的一道Java面试题. 其实笔者本人对这类框架源码题还是持一定的怀疑态度的. 如果笔者作为面试官,可能会问一些诸如"如果注入的属性为null, ...
- Spring 是怎么处理循环依赖的?
Java语法中的循环依赖 首先看一个使用构造函数的循环依赖,如下: public class ObjectA { private ObjectB b; public ObjectA(ObjectB b ...
- Spring 使用@Async出现循环依赖Bean报错的解决方案
初现端倪 Caused by:org.springframework.beans.factory.BeanCurrentlyInCreationException: Errorcreating bea ...
- Spring源码解析——循环依赖的解决方案
一.前言 承接<Spring源码解析--创建bean>.<Spring源码解析--创建bean的实例>,我们今天接着聊聊,循环依赖的解决方案,即创建bean的ObjectFac ...
随机推荐
- istio kiali 亲和性调度
一.节点调度 在开始 kiali 亲和性调度之前,先演示一个简单的例子介绍 pod 选择调度到指定 node: 节点打标 使用命令查看当前所有 k8s 节点: [root@k8s-master ~]# ...
- 申请免费域名并配置DNS解析及CDN加速
标题: 申请免费域名并配置DNS解析及CDN加速 作者: 梦幻之心星 sky-seeker@qq.com 标签: [#申请,#免费,#域名,#DNS解析,#CDN加速,#子域名] 目录: [网站] 日 ...
- 4.5万字手把手教你实现MySQL TB级数据存储!!
写在前面 业界对系统的高可用有着基本的要求,简单的说,这些要求可以总结为如下所示. 系统架构中不存在单点问题. 可以最大限度的保障服务的可用性. 一般情况下系统的高可用可以用几个9来评估.所谓的几个9 ...
- 【MySQL 基础】MySQL必知必会
MySQL必知必会 简介 <MySQL必知必会>的学习笔记和总结. 书籍链接 了解SQL 数据库基础 什么是数据库 数据库(database):保存有组织的数据的容器(通常是一个文 件或一 ...
- Linux 入门教程:00 Background
Linux 为何物? 就是一个操作系统. Linux 历史: 操作系统始于二十世纪五十年代,当时的操作系统能运行批处理程序.批处理程序不需要用户的交互,它从文件或者穿孔卡片读取数据,然后输出到另外一个 ...
- (一)React Ant Design Pro + .Net5 WebApi:先搞定服务器,顺手装个Nginx
腾讯云搞定服务器,具体过程就不赘述了,文档都有,咨询客服或者自行百度,体验一下过程. 一. 服务器 1. 云服务器 cvm 1核2G centos8.0 2. 域名注册 www.homejok.com ...
- 【Oracle】整库导出后怎么恢复到指定用户的指定表
在导出的时候,整库导出 这里使用的是dba权限 $exp "'/ as sysdba'" file=full20180227.dmp log=exp_full20180227.lo ...
- SAP中数据库表长度的界定
SAP中,如何查看表和关键字的长度?通过SE11菜单栏Extras->table width 可以看到.然而SAP在系统也会将表分类,特别是在可扩展的表维护视图中,分为如下几类 ult ...
- springmvc 字符串转日期格式
http://www.mamicode.com/info-detail-2485490.html
- 1.2V转3V芯片,电路图很少就三个元件
1.2V的镍氢电池由于稳定高,应用产品也是很广,但是由于电压低,需要1.2V转3V芯片,来将1.2V的电压升压转3V,稳定输出供电. 一般性的1.2V转3V芯片,都是用PW5100比较多,固定输出电压 ...