前言
上篇我们分享了Spring AOP的相关源码,今天我们分享一个和Spring AOP联系非常紧密的话题---Spring事务。很多人认为事务很简单,但是往往在工作中遇到一些事务的坑(尤其是事务方法中嵌套其它事务方法一起使用时)之后,我们却不知道问题产生的原因和如何有效的解决。这就需要去分析Spring的核心源码,最终踏实地找到问题的原因和解决思路。
注解事务运行流程
先来看Spring事务的底层运行流程
核心对象关系
一、事务配置相关 TransactionManagementConfigurationSelector:配置启动事务启动(EnableTransactionManagement)时,导入注册的配置bean。它包括AutoProxyRegistrar和ProxyTransactionManagementConfiguration两大配置块。 AutoProxyRegistrar:负责依赖注入事务的相关属性配置和注入事务入口类(InfrastructureAdvisorAutoProxyCreator类); ProxyTransactionManagementConfiguration:负责注入事务相关的Bean, 包括:事务切面Bean(BeanFactoryTransactionAttributeSourceAdvisor)、TransactionAttributeSource(事务配置属性bean)和TransactionInterceptor(事务拦截器bean)等; 二、事务运行拦截相关 AopProxy:Spring AOP 动态代理的基接口,负责定义Proxy的基础行为; MethodInterceptor:Spring AOP调用链中拦截器的内部核心接口,所有类型的切面最终都会最终包装成此接口触发统一拦截; TransactionInterceptor:Spring事务拦截器的核心业务实现,AOP调用链也最终触发它的invoke方法; TransactionManager:Spring管理的基接口,作为子接口上层接口区分,并没有定义实际的事务行为能力; PlatformTransactionManager:继承TransactionManager,定义事务和基础行为; AbstractPlatformTransactionManager:负责实现整个事务管理和运行过程中的公共行为和通用实现逻辑,它继承至PlatformTransactionManager接口;
源码实现
接下来我们来分块分析一下,Spring事务的源码,其中的一些坑和重要结论会在这个过程分享。 一、事务配置 TransactionManagementConfigurationSelector.selectImports()负责定义外部加入spring容器的配置类 此方法最终在ConfigurationClassParser中被解析并最终实例化为bean AutoProxyRegistrar.registerBeanDefinitions()把InfrastructureAdvisorAutoProxyCreator注册beandefinition ProxyTransactionManagementConfiguration.transactionAdvisor()注入事务切面
二、事务创建 实际运行入口之一(还有cglib):JdkDynamicAopProxy..invoke() ReflectiveMethodInvocation.proceed()切面调用链处理核心方法 TransactionInterceptor.invoke()从这里触发事务拦截 TransactionAspectSupport.invokeWithinTransaction()实现Spring事务的核心业务 TransactionAspectSupport.determineTransactionManager()定义指定的事务管理器 对于事务管理器,默认使用DataSourceTransactionManager(可配置数据源的事务管理器),也可自定义事务管理器,然后配置数据源即可。 createTransactionIfNecessary()创建事务信息TransactionInfo:其中包括数据源、事务连接(ConnectionHolder)、事务状态、连接缓存等; DataSourceTransactionManager.doGetTransaction()获取事务对象:里面包含连接包装和缓存 TransactionSynchronizationManager.getResource()连接线程级缓存:确保当前线程拿到唯一的数据连接 AbstractPlatformTransactionManager.startTransaction()开启一个新事务 只有newTransaction为true时,spring才会做实际的提交或回滚,这里是一个很重要的点。很多嵌套事务的坑,都是这里没有理解清楚。 DataSourceTransactionManager.doBegin()开启事务核心源码 TransactionSynchronizationManager.bindResource()设置当前连接和数据源的线程级绑定 三、事务回滚 对于非编程式事务而言,Spring事务的核心实现其实就是用try / catch 包裹事务提交和回滚的范式而已,但提交和回滚里面的包装大有讲究。 TransactionAspectSupport.completeTransactionAfterThrowing()事务回滚实现 以上截图中doSetRollbackOnly(),不会在连接上实际设置回滚点,只打个标记(当前事务需要回滚, commit时会使用该标记)!
四、事务提交 AbstractPlatformTransactionManager.commit()事务实际提交源码这里 cleanupAfterCompletion()回收连接或恢复事务,这个点耐人寻味:当事务传播属性为Require_New时,会暂时挂起之前的连接,然后创建新连接;当新连接提交或回滚后,通过这个方法恢复之前连接和状态!!!!
OK事务到这里,我根据源码分享一些关于嵌套子事务的甜点:
- 嵌套事务过程,中间有任何异常,只要没被屏蔽,则都会上抛,不会再执行后续代码;
- 只要设置Require_New事务传播属性,则每个Transactional注解方法内部都会单独提交或回滚;
- 嵌套事务会在除首个Transactional注解外的子事务中创建savepoint回滚点;
- 设置savepoint回滚点后,回滚时会回滚包括当前方法之前的所有事务;若只需回滚当前方法,则在最外层事务方法加try{}catch(){//不抛出异常};
- 每个事务的对象都是不一样的,事务状态可能不一样,但连接可能是同一个;
总结
今天的Spring事务源码就暂时分享到这里,事务这块只有一层的事务很简单,但当嵌套了多个(层)子事务(而且每个子事务的事务传播属性可能不一样的情况下)时就当另当别论了。需要我们根据源码的规律去分析每层子事务该如何流转!!所以需要把这块的源码搞熟,遇到复杂的嵌套事务分析原因,那问题就不大了。更新spring源码的干货请继续关注。