前言
在我们日常使用这些框架的时候,还有哪些疑问呢?就楼主而言,楼主已经明白了 IOC ,AOP 的原理,也明白了 Mybatis 的原理,也明白了 Spring 和 Mybatis 是如何整合的。但是,我们漏掉了 JavaEE 中一个非常重要的特性:事务。事务是 Java 程序员开发程序时不可避免的问题。我们就不讨论 ACID 的事务特性,楼主这里假定大家都已经了了解了事务的原理。那么,我们今天的任务是剖析源码,看看Spring 是怎么运行事务的,并且是基于当前最流行的SpringBoot。还有,我们之前剖析Mybatis 的时候,也知道,Mybatis 也有事务,那么,他俩融合之后,事务是交给谁的?又是怎么切换的?今天这几个问题,我们都要从源码中找到答案。
1. Spring 的事务如何运行?
如果各位使用过SpringBoot ,那么就一定知道如何在Spring中使用注解,比如在一个类或者一个方法上使用 @Transactional 注解,在一个配置类上加入一个 @
EnableTransactionManagement 注解代表启动事务。而这个配置类需要实现 TransactionManagementConfigurer 事务管理器配置接口。并实现 annotationDrivenTransactionManager 方法返回一个包含了 配置好数据源的 DataSourceTransactionManager 事务对象。这样就完成了事务配置,就可以在Spring使用事务的回滚或者提交功能了。
这个 saveList 方法就在Spring事务的控制之下,如果发生了异常,就会回滚事务。如果各位知道更多的Spring的事务特性,可以在注解中配置,比如什么异常才能回滚,比如超时时间,比如隔离级别,比如事务的传播。就更有利于理解今天的文章了。
我们基于一个 Junit 测试用例,来看看Spring的事务时如何运行的。
在测试用例中执行该方法,参数时一个空的List,这个Sql的运行肯定是失败的。我们主要看看他的运行过程。我们讲断点打在该方法上。断点进入该方法。
注意,
dataCollectionShareService 对象已经被 Cglib 代理了,那么他肯定会走 DynamicAdvisedInterceptor 的 intercept 方法,我们断点进入该方法查看,这个方法我们已经很属性了,该方法中,最重要的事情就是执行通知器或者拦截器的方法,那么,该代理有通知器吗?
有一个通知器。是什么呢?
一个事务拦截器,也就是说,如果通知器链不为空,就会依次执行通知器链的方法。那么 TransactionInterceptor 到底是什么呢?
该类实现了通知器接口,也实现类 MethodInterceptor 接口,并实现了该接口的 invoke 方法,在 DynamicAdvisedInterceptor 的 intercept 方法中,最终会调用每个 MethodInterceptor 的 invoke 方法,那么,TransactionInterceptor 的 invoke 方法是如何实现的呢?
invoke 方法中会调用自身的 invokeWithinTransaction 方法,看名字,该方法和事务相关。该方法参数是由目标方法,目标类,一个回调对象构成。 那么我们就进入该方法查看,该方法很长:
/**
* General delegate for around-advice-based subclasses, delegating to several other template
* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
* as well as regular {@link PlatformTransactionManager} implementations.
* @param method the Method being invoked
* @param targetClass the target class that we're invoking the method on
* @param invocation the callback to use for proceeding with the target invocation
* @return the return value of the method, if any
* @throws Throwable propagated from the target invocation
*/
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, tx