Spring事务详解
一、事务执行流程
- 开启事务
- 执行DML语句
- 提交事务/回滚事务
说明:如果多个DML语句是同一个连接对象操作的,他们的算作同一个事务,前提是关闭自动提交。
二、Spring事务切面
事务切面开启注解@EnableTransactionManagement,由注解上的@Import(TransactionManagementConfigurationSelector.class)
来到selectImports方法。PROXY代表JDK事务,引入了AutoProxyRegistrar和ProxyTransactionManagementConfiguration。
AutoProxyRegistrar的作用是注册aop的入口类InfrastructureAdvisorAutoProxyCreator,把属性设置到入口类中,最终会copy到proxyFactory中。Bean实例化最后一步判断是否有切面,如果有则生成代理。
ProxyTransactionManagementConfiguration中定义了事务切面BeanFactoryTransactionAttributeSourceAdvisor。
首先在事务切面里面设置处理事务属性对象TransactionAttributeSource,是由下面transactionAttributeSource方法创建的。这个是@Transactional注解的属性解析类,解析完注解后将属性封装成transactionAttribute对象。在transactionAttributeSource中可以看到它是AnnotationTransactionAttributeSource对象。
enableTx是@EnableTransactionManagement中的排序属性,是一个map,根据这个属性可以排序切面。
事务切面会设置advice,是TransactionInterceptor类型,由下面的transactionInterceptor定义,它继承了MethodInterceptor。
在transactionInterceptor中可以看到如果txManager不为空,advice就设置了事务管理器,它是在父类AbstractTransactionManagementConfiguration中定义的。由setConfigurers可以依赖注入TransactionManagementConfigurer,在annotationDrivenTransactionManager方法中返回事务管理器,这就要求定义一个类实现TransactionManagementConfigurer,如下图所示。启动过程中没有txManager无影响,一般也不用这种方式,常用的会在后面说。
三、事务切面的Pointcut和Advice
每个切面都会有Pointcut和Advice。首先看事务切面的PointCut,它定义在BeanFactoryTransactionAttributeSourceAdvisor中,在代理生成之前会调用ClassFilter的matches和MethodMatcher的matches。
ClassFilter。TransactionAttributeSourcePointcut第一行就定义了ClassFilter为TransactionAttributeSourceClassFilter,但是这个
ClassFilter其实什么都没做。
看到它的matches方法,关键代码是tas.isCandidateClass(clazz)
。这里的TransactionAttributeSource为AnnotationTransactionAttributeSource,看到它的isCandidateClass。
isCandidateClass中循环调用了annotationParsers容器中的TransactionAnnotationParser.isCandidateClass。annotationParsers是在上面AnnotationTransactionAttributeSource中定义的,这里起作用的也就只有
SpringTransactionAnnotationParser。
在SpringTransactionAnnotationParser的isCandidateClass中可以看到,它是在找java.开头的注解和java.开头的类。@Transactional注解是Spring定义的,事务相关的类是自定义的,所以都不符合,返回true。于是ClassFilter的matches一直都是返回true。
MethodMatcher。TransactionAttributeSourcePointcut继承了StaticMethodMatcherPointcut,StaticMethodMatcherPointcut继承了StaticMethodMatcher,StaticMethodMatcher实现了MethodMatcher,所以TransactionAttributeSourcePointcut本身就是MethodMatcher,它里面定义了MethodMatcher的matches方法。
matches方法会调用2次, 第一次是找切面的过程,代理对象生成之前会调用一次,第二次是在走拦截器链的时候又会被调用。ClassFilter的matches是一直返回为true的,也就是说只要方法上面能拿到@Transactional注解就生成代理。这里是拿事务属性,拿事务属性就相当于有@Transactional注解。
看到AbstractFallbackTransactionAttributeSource的getTransactionAttribute,首先从缓存中取TransactionAttribute,如果不为空直接返回,如果为空,则需要在computeTransactionAttribute中得到TransactionAttribute。
computeTransactionAttribute中首先判断方法是否为public,如果是非public方法则返回null不会生成代理。获取原始方法,AopUtils.getMostSpecificMethod(method, targetClass);
很有用,它总能拿到原始方法(因为生成代理之后,一般拿到的都是方法的代理)。
获取方法上面@Transactional注解的属性,点进去后来到AnnotationTransactionAttributeSource的determineTransactionAttribute,方法中主要是通过SpringTransactionAnnotationParser的parseTransactionAnnotation,拿到@Transactional的属性并封装成TransactionAttribute对象。其中,attributes相当于map,存的都是@Transactional的属性,parseTransactionAnnotation中拿到属性封装成对象。
如果方法上没有注解,再通过findTransactionAttribute去类上找注解。点进去可以看到它也是来到AnnotationTransactionAttributeSource的determineTransactionAttribute,逻辑和上面从方法上找注解是一样的。
再回到AbstractFallbackTransactionAttributeSource的getTransactionAttribute,如果TransactionAttribute不为空,将它放入缓存,然后返回。拿到了事务属性返回true,生成代理。
Advice。事务切面的Advice即TransactionInterceptor,看到它的invoke方法,invocation::proceed
很明显就是一个链式调用。
进入invokeWithinTransaction。因为ProxyTransactionManagementConfiguration的transactionInterceptor中在依赖注入后将TransactionAttributeSource设置到advice中了,所以能通过getTransactionAttributeSource拿到事务属性类AnnotationTransactionAttributeSource,然后获取事务属性和事务管理器。
determineTransactionManager中可以看到如果事务管理器为空,从Spring容器中拿到TransactionManager类型的类作为事务管理器。也就是说,只要自定义一个TransactionManager,注册到Spring容器中,就能引入事务管理器。下面白图中的DataSourceTransactionManager就是TransactionManager,一般都是用这种方式引入事务管理器。
asPlatformTransactionManager是校验事务管理器,methodIdentification是获取方法名称。
四、注解事务的源码分析
还是在invokeWithinTransaction方法中。
进入createTransactionIfNecessary后,tm.getTransaction(txAttr);
主要是开启事务。
进入tm.getTransaction(txAttr);
再进入doGetTransaction();
DataSourceTransactionObject是事务对象,里面有个newConnectionHolder标识,标识是否是新连接。事务肯定是跟连接对象挂钩的,父类JdbcTransactionObjectSupport中定义了当前连接对象的包装类ConnectionHolder,可以理解为数据库连接对象。JdbcTransactionObjectSupport还定义了连接对象属性相关的信息,包括隔离级别、是否只读、是否允许回滚。所以DataSourceTransactionObject是跟连接对象有关的一些包装。
接着看doGetTransaction方法,txObject.setSavepointAllowed(isNestedTransactionAllowed());
含义为是否创建回滚点,isNestedTransactionAllowed()默认返回的是true。
obtainDataSource()是获取数据源对象,即上面例子中设置的数据源,TransactionSynchronizationManager.getResource是根据dataSource从ThreadLocal中获取到连接对象,第一次是没有值的,为null。ThreadLocal的结构是Map<DataSource, ConnectionHolder>
。
拿到连接对象后放到事务对象中,设置newConnectionHolder为false。
isExistingTransaction(transaction)
是判断事务对象中的连接对象是否为空。第一次进来connectionHolder为空,所以if不会走,看到下面的代码。
第一次进来会首先判断事务的传播属性,事务的传播属性是用来控制事务流转的。传播属性为REQUIRED、REQUIRES_NEW、NESTED执行下面的代码。传播属性默认为REQUIRED。
首先将事务挂起,然后执行startTransaction。
startTransaction中下面的代码是创建一个新的事务状态。newTransaction为true,这是核心。DefaultTransactionStatus对象主要是标识事务的一些属性,是否是最新事务,是否是旧的事务,Spring可以通过它来控制事务的传播。
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
在newTransactionStatus方法中可以看到把事务对象、状态标识、是否挂起等属性都传到了DefaultTransactionStatus中返回。
doBegin(transaction, definition);
开启事务。首先判断事务对象中有没有连接对象,如果没有连接对象,从dataSource中拿到连接对象,包装成ConnectionHolder后设置到事务对象中,并把newConnectionHolder设置为true。
设置有事务标识,设置是否只读连接和事务隔离级别到连接对象中,设置该连接的上一个隔离级别。如果是自动提交,关闭自动提交。
把事务活跃状态设置为true,设置事务超时时间。首先判断newConnectionHolder是否为true,现在是为true的。TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
是绑定线程、dataSource、连接对象。在这里可以看到线程对应的是map,map中是数据源对应连接对象。
从doBegin出来后来到prepareSynchronization,这里是改变TransactionSynchronizationManager中的事务状态。DefaultTransactionStatus是Spring内部流转的事务状态,TransactionSynchronizationManager相当于开发人员使用的事务状态工具类,可以拿到事务的流转状态,是否已经提交、回滚。