Spring的Cache组件 - @Transactional + @CacheEvict 在同一方法上声明,删除缓存异常是否导致事务回滚?

本文详细探讨了在Spring中,@Transactional和@CacheEvict同时使用时,删除缓存异常不会导致事务回滚的原因。这涉及到BeanFactoryTransactionAttributeSourceAdvisor和BeanFactoryCacheOperationSourceAdvisor这两个拦截器的执行顺序,以及SpringBoot的AutoConfiguration加载顺序对拦截器创建的影响。文章指出,事务和缓存拦截器的执行顺序取决于@EnableTransactionManagement和@EnableCaching的声明顺序,以及AutoConfiguration的导入顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring 中的 Cache 组件 (2) - @Transactional + @CacheEvict 同时使用时,删除缓存异常并不会导致事务被回滚! - noob_fly的个人空间 - OSCHINA - 中文开源技术交流社区https://my.oschina.net/u/3434392/blog/5577300

本文基于上文中的表述,补全BeanFactoryTransactionAttributeSourceAdvisor(TransactionInterceptor)、BeanFactoryCacheOperationSourceAdvisor(CacheInterceptor)拦截器的执行顺序成因。

引文

Spring 中的 Cache 组件 (1)https://my.oschina.net/u/3434392/blog/3068206  简述了 cache 相关注解的使用方式;

redis (1) - 缓存: 击穿 & 穿透 & 雪崩https://my.oschina.net/u/3434392/blog/3012132  简述了缓存一致性问题;

Bean 创建(2)- 三级缓存解决循环依赖问题https://my.oschina.net/u/3434392/blog/4514135阐述spring 容器在创建bean实例的过程,

  1. 如果有下层依赖,从时序上一般而言会先完成该下层依赖对象的创建。
  2. 在每个bean的创建过程最末尾,会使用 AnnotationAwareAspectJAutoProxyCreator#wrapIfNecessary 判定创建代理对象。它首先会执行 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean ->#findEligibleAdvisors 过程找到有效的Advisor集合: 收集容器里所有的Advisors >>> 筛选出符合条件的 >>> 排序。

AnnotationAwareAspectJAutoProxyCreator与AbstractAdvisingBeanPostProcessor在创建代理的方式区别 - noob_fly的个人空间 - OSCHINA - 中文开源技术交流社区https://my.oschina.net/u/3434392/blog/5561343阐述了spring两种主要创建实例代理的方式。

删除缓存异常是否会导致事务被回滚取决于事务、缓存拦截器的执行先后

在SpringBoot的启动类上,

  1. 对于事务,可以声明@EnableTransactionManagement来引入ProxyTransactionManagementConfiguration来创建切面拦截处理BeanFactoryTransactionAttributeSourceAdvisor;
  2. 对于缓存,可以声明@EnableCaching来引入ProxyCachingConfiguration装载类来创建BeanFactoryCacheOperationSourceAdvisor;

同时,在'spring-boot-autoconfigure' jar包的spring.factories文件里声明自动装载配置类'org.springframework.boot.autoconfigure.EnableAutoConfiguration',先定义了CacheAutoConfiguration,后定义了TransactionAutoConfiguration。

但在它们各自的实现里有差异:

  • TransactionAutoConfiguration的实现: 如果没有装载过AbstractTransactionManagementConfiguration(ProxyTransactionManagementConfiguration), 就由它来声明@EnableTransactionManagement;  也就是说即使没有显示声明@EnableTransactionManagement, 也会默认开启事务控制

  • 而CacheAutoConfiguration并不会默认开启缓存控制切面拦截。 它只是默认Import了不同介质的缓存装载类。


 接下来分析一下加载顺序。

示例一:

@SpringBootApplication
@EnableCaching
BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans()​​​​

示例二:

@EnableCaching
@SpringBootApplication

以上2个示例可见:如果不显示声明 @EnableTransactionManagement ,扫描顺序 internalCacheAdvisor 都是先于 internalTransactionAdvisor 。

因为@SpringBootApplication(@EnableAutoConfiguration)  引入的AutoConfigurationImportSelector继承了DeferredImportSelector。在Spring启动初期解析资源装载类时ConfigurationClassParser#parse 里先执行到【#processConfigurationClass ->#processImports】 :判定是延时的DeferredImportSelector,则交给DeferredImportSelectorHandler,再在最该过程最后执行deferredImportSelectorHandler#process()处理它。详见启动 ApplicationContext 加载配置 (2) - ConfigurationClassPostProcessor (最重要的 BeanFactoryPostProcessor) 源码简析 二https://my.oschina.net/u/3434392/blog/4684048

这也就解释了:当在BeaFactory里找Advisor.class定义的名称时,先扫描到Cache后Transaction。

 但它们装载顺序实际上是:先ProxyTransactionManagementConfiguration,后ProxyCachingConfiguration!

为什么呢?接着看BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans()​​​​拿到了两个Advisor的名称后接着做的事情。

在测试过程中发现:

当一开始要#getBean获取internalCacheAdvisor实例时,却循环再次进入该方法过程去拿了internalTransactionAdvisor,导致时序上先执行了ProxyTransactionManagementConfiguration!

结合本文开头的阐述:

所以,当要创建internalCacheAdvisor实例时,判定是否要创建它的代理时,又再次进入到BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans()​​​​过程。

在上图里的1处代码,beanFactory#isCurrentlyInCreation判定internalCacheAdvisor已经在创建过程中了则跳过;当判定到internalTransactionAdvisor时就会去执行它的#getBean过程。这样的话,ProxyTransactionManagementConfiguration会先被执行到!


按上述规律,可以解释下文各示例。

示例三:

@EnableCaching
@EnableTransactionManagement
@SpringBootApplication

装载顺序是:先ProxyTransactionManagementConfiguration,后ProxyCachingConfiguration; 

示例四:

@EnableTransactionManagement
@EnableCaching
@SpringBootApplication

先 ProxyCachingConfiguration后ProxyTransactionManagementConfiguration! 

示例四:

@SpringBootApplication
@EnableTransactionManagement
@EnableCaching

先 ProxyCachingConfiguration后ProxyTransactionManagementConfiguration! 

示例五:

@SpringBootApplication
@EnableCaching
@EnableTransactionManagement

先ProxyTransactionManagementConfiguration,后ProxyCachingConfiguration; 


最终BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans()​​​​返回的顺序如下:

  • 先ProxyTransactionManagementConfiguration,后ProxyCachingConfiguration的情况下如下图

  •  先 ProxyCachingConfiguration后ProxyTransactionManagementConfiguration的情况下如下图
AbstractAdvisorAutoProxyCreator#findEligibleAdvisors(sort前后是没变化,影响可忽略)

 所以:

删除缓存异常是否会导致事务被回滚,取决于事务、缓存拦截器的执行先后!而这与:

  1. @EnableTransactionManagement、@EnableCaching的声明顺序 正相关 !
  2. 如果不显示声明 @EnableTransactionManagement ,缓存拦截器的要先执行 !
  3. 与ProxyCachingConfiguration、ProxyTransactionManagementConfiguration的装载执行先后 逆相关 !
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值