H5W3
当前位置:H5W3 > java > 正文

【Java】Spring 事务、异步和循环依赖有什么关系?

Spring 事务、异步和循环依赖有什么关系?

程序员小航发布于 今天 09:28

前言

在循环依赖中有一种循环依赖,就是自注入:自己依赖自己。

【Java】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 {
// 内部是事务逻辑
}
}

是不是发现很神奇的事情,事务生效了。

其实这里注入自己,其实是注入的一个代理对象,调事务,也是调的代理对象的事务,所以事务生效。

异步的自注入

发现 @Transactional 注解可以自注入解决事务失效的问题,在某次开发中,自然而然想到 @Async 异步是不是也可以自注入解决循环依赖的问题。

NO, NO, NO……

事实告诉我们是不可以的!

【Java】Spring 事务、异步和循环依赖有什么关系?

从错误开始着手:

【Java】Spring 事务、异步和循环依赖有什么关系?

开始往上面反推 exposedObject == bean 是这一块出了问题。

也就是说异步的时候,再次从二级缓存中获取的和初始的不相同。

Object earlySingletonReference = getSingleton(beanName, false);

【Java】Spring 事务、异步和循环依赖有什么关系?

这一次获取的时候发现不同所以报错。

那就开始 Debug, 按照循环依赖的逻辑,执行到 populateBean 时,属性赋值,发现有依赖自己,此时会创建自己。

执行 singleton.getObject 方法

【Java】Spring 事务、异步和循环依赖有什么关系?

【Java】Spring 事务、异步和循环依赖有什么关系?

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

【Java】Spring 事务、异步和循环依赖有什么关系?

然后开始执行异步的 AsyncAnnotationBeanPostProcessor 判断为 false。所以没有执行异步的生成代理对象逻辑。

那就继续往下看

【Java】Spring 事务、异步和循环依赖有什么关系?

进入到 initializeBean 的逻辑,有一部分叫做 applyBeanPostProcessorsAfterInitialization

【Java】Spring 事务、异步和循环依赖有什么关系?

循环执行后置处理器:

【Java】Spring 事务、异步和循环依赖有什么关系?

【Java】Spring 事务、异步和循环依赖有什么关系?

发现执行完 AsyncAnnotationBeanPostProcessor 这个 PostProcessor 后,对象被改变了。从而导致二级缓存和当前的 Bean 不同。

以上也就是为什么 @Async 自调用不可以,因为在后面初始化阶段被代理修改了对象。

@Transactional 为什么可以呢?

【Java】Spring 事务、异步和循环依赖有什么关系?

【Java】Spring 事务、异步和循环依赖有什么关系?

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

【Java】Spring 事务、异步和循环依赖有什么关系?

事务的处理器 PersistenceExceptionTranslationPostProcessor 也没有执行。

继续 Debug 关注 applyBeanPostProcessorsAfterInitialization

【Java】Spring 事务、异步和循环依赖有什么关系?

执行结束,发现 Bean 没有发生改变。

总结

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

【Java】Spring 事务、异步和循环依赖有什么关系?

可以看出图中有两处会执行 BeanPostProcessor :

  1. 在 singletonFactory.getObject 时,如果是 SmartInstantiationAwareBeanPostProcessor 的子类会执行 getEarlyBeanReference 方法。
  2. 在 initializeBean 的 applyBeanPostProcessorsAfterInitialization 时会执行所有 BeanPostProcessor 的 postProcessAfterInitialization 的方法。

而这两处都有可能生成代理对象, @Transactional 是在 getEarlyBeanReference 处生成的代理对象,所以后面判断 Bean 是否被改变时为 true,而 @Async 是在后面异步生成了代理对象,所以判断不通过。

至此,分析完毕,错误之处,欢迎指正。

相关推荐

  • Spring 动态代理时是如何解决循环依赖的?为什么要使用三级缓存?
  • Spring 是如何解决循环依赖的?
  • Spring 自调用事务失效,你是怎么解决的?
javaspring源码事务
阅读 41发布于 今天 09:28
本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议

小航的技术笔记
个人公众号:『 liuzhihangs 』
avatar

程序员小航

<b>个人公众号:『 程序员小航 』</b>

10 声望
1 粉丝

0 条评论
得票时间

avatar

程序员小航

<b>个人公众号:『 程序员小航 』</b>

10 声望
1 粉丝

宣传栏

前言

在循环依赖中有一种循环依赖,就是自注入:自己依赖自己。

【Java】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 {
// 内部是事务逻辑
}
}

是不是发现很神奇的事情,事务生效了。

其实这里注入自己,其实是注入的一个代理对象,调事务,也是调的代理对象的事务,所以事务生效。

异步的自注入

发现 @Transactional 注解可以自注入解决事务失效的问题,在某次开发中,自然而然想到 @Async 异步是不是也可以自注入解决循环依赖的问题。

NO, NO, NO……

事实告诉我们是不可以的!

【Java】Spring 事务、异步和循环依赖有什么关系?

从错误开始着手:

【Java】Spring 事务、异步和循环依赖有什么关系?

开始往上面反推 exposedObject == bean 是这一块出了问题。

也就是说异步的时候,再次从二级缓存中获取的和初始的不相同。

Object earlySingletonReference = getSingleton(beanName, false);

【Java】Spring 事务、异步和循环依赖有什么关系?

这一次获取的时候发现不同所以报错。

那就开始 Debug, 按照循环依赖的逻辑,执行到 populateBean 时,属性赋值,发现有依赖自己,此时会创建自己。

执行 singleton.getObject 方法

【Java】Spring 事务、异步和循环依赖有什么关系?

【Java】Spring 事务、异步和循环依赖有什么关系?

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

【Java】Spring 事务、异步和循环依赖有什么关系?

然后开始执行异步的 AsyncAnnotationBeanPostProcessor 判断为 false。所以没有执行异步的生成代理对象逻辑。

那就继续往下看

【Java】Spring 事务、异步和循环依赖有什么关系?

进入到 initializeBean 的逻辑,有一部分叫做 applyBeanPostProcessorsAfterInitialization

【Java】Spring 事务、异步和循环依赖有什么关系?

循环执行后置处理器:

【Java】Spring 事务、异步和循环依赖有什么关系?

【Java】Spring 事务、异步和循环依赖有什么关系?

发现执行完 AsyncAnnotationBeanPostProcessor 这个 PostProcessor 后,对象被改变了。从而导致二级缓存和当前的 Bean 不同。

以上也就是为什么 @Async 自调用不可以,因为在后面初始化阶段被代理修改了对象。

@Transactional 为什么可以呢?

【Java】Spring 事务、异步和循环依赖有什么关系?

【Java】Spring 事务、异步和循环依赖有什么关系?

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

【Java】Spring 事务、异步和循环依赖有什么关系?

事务的处理器 PersistenceExceptionTranslationPostProcessor 也没有执行。

继续 Debug 关注 applyBeanPostProcessorsAfterInitialization

【Java】Spring 事务、异步和循环依赖有什么关系?

执行结束,发现 Bean 没有发生改变。

总结

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

【Java】Spring 事务、异步和循环依赖有什么关系?

可以看出图中有两处会执行 BeanPostProcessor :

  1. 在 singletonFactory.getObject 时,如果是 SmartInstantiationAwareBeanPostProcessor 的子类会执行 getEarlyBeanReference 方法。
  2. 在 initializeBean 的 applyBeanPostProcessorsAfterInitialization 时会执行所有 BeanPostProcessor 的 postProcessAfterInitialization 的方法。

而这两处都有可能生成代理对象, @Transactional 是在 getEarlyBeanReference 处生成的代理对象,所以后面判断 Bean 是否被改变时为 true,而 @Async 是在后面异步生成了代理对象,所以判断不通过。

至此,分析完毕,错误之处,欢迎指正。

相关推荐

  • Spring 动态代理时是如何解决循环依赖的?为什么要使用三级缓存?
  • Spring 是如何解决循环依赖的?
  • Spring 自调用事务失效,你是怎么解决的?

本文地址:H5W3 » 【Java】Spring 事务、异步和循环依赖有什么关系?

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址