本文基于上文中的表述,补全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实例的过程,
- 如果有下层依赖,从时序上一般而言会先完成该下层依赖对象的创建。
- 在每个bean的创建过程最末尾,会使用 AnnotationAwareAspectJAutoProxyCreator#wrapIfNecessary 判定创建代理对象。它首先会执行 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean ->#findEligibleAdvisors 过程找到有效的Advisor集合: 收集容器里所有的Advisors >>> 筛选出符合条件的 >>> 排序。
AnnotationAwareAspectJAutoProxyCreator与AbstractAdvisingBeanPostProcessor在创建代理的方式区别 - noob_fly的个人空间 - OSCHINA - 中文开源技术交流社区https://my.oschina.net/u/3434392/blog/5561343阐述了spring两种主要创建实例代理的方式。
删除缓存异常是否会导致事务被回滚取决于事务、缓存拦截器的执行先后
在SpringBoot的启动类上,
- 对于事务,可以声明@EnableTransactionManagement来引入ProxyTransactionManagementConfiguration来创建切面拦截处理BeanFactoryTransactionAttributeSourceAdvisor;
- 对于缓存,可以声明@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

示例二:
@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的情况下如下图

所以:
删除缓存异常是否会导致事务被回滚,取决于事务、缓存拦截器的执行先后!而这与:
- @EnableTransactionManagement、@EnableCaching的声明顺序 正相关 !
- 如果不显示声明 @EnableTransactionManagement ,缓存拦截器的要先执行 !
- 与ProxyCachingConfiguration、ProxyTransactionManagementConfiguration的装载执行先后 逆相关 !