事务之四:Spring事务--原理
一、Spring事务的基本原理
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行:
- 获取连接 Connection con = DriverManager.getConnection()
- 开启事务con.setAutoCommit(true/false);
- 执行CRUD
- 提交事务/回滚事务 con.commit() / con.rollback();
- 关闭连接 conn.close();
使用Spring的事务管理功能后,我们可以不再写步骤 2 和 4 的代码,而是由Spirng 自动完成。 那么Spring是如何在我们书写的 CRUD 之前和之后开启事务和关闭事务的呢?解决这个问题,也就可以从整体上理解Spring的事务管理实现原理了。下面简单地介绍下,注解方式为例子:
- 配置文件开启注解驱动,在相关的类和方法上通过注解@Transactional标识。
- spring 在启动的时候会去解析生成相关的bean,这时候会查看拥有相关注解的类和方法,并且为这些类和方法生成代理,并根据@Transaction的相关参数进行相关配置注入,这样就在代理中为我们把相关的事务处理掉了(开启正常提交事务,异常回滚事务)。
- 真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
二、工作原理及实现
a、划分处理单元——IOC
由于spring解决的问题是对单个数据库进行局部事务处理的,具体的实现首相用spring中的IOC划分了事务处理单元。并且将对事务的各种配置放到了ioc容器中(设置事务管理器,设置事务的传播特性及隔离机制)。
b、AOP拦截需要进行事务处理的类
Spring事务处理模块是通过AOP功能来实现声明式事务处理的,具体操作(比如事务实行的配置和读取,事务对象的抽象),用TransactionProxyFactoryBean接口来使用AOP功能,生成proxy代理对象,通过TransactionInterceptor完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。
读取ioc容器事务配置属性,转化为spring事务处理需要的内部数据结构(TransactionAttributeSourceAdvisor),转化为TransactionAttribute表示的数据对象。
c、对事物处理实现(事务的生成、提交、回滚、挂起)
spring委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSource数据源支持、hibernate数据源事务处理支持、JDO数据源事务处理支持,JPA、JTA数据源事务处理支持。这些支持都是通过设计PlatformTransactionManager、AbstractPlatforTransaction一系列事务处理的支持。
为常用数据源支持提供了一系列的TransactionManager。
d、结合
PlatformTransactionManager实现了TransactionInterception接口,让其与TransactionProxyFactoryBean结合起来,形成一个Spring声明式事务处理的设计体系。
三、 核心接口
Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略。
Spring事务管理涉及的接口的联系如下:
3.1 事务管理器
Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。此接口的内容如下:
Public interface PlatformTransactionManager()...{ // 由TransactionDefinition得到TransactionStatus对象 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; // 提交 Void commit(TransactionStatus status) throws TransactionException; // 回滚 Void rollback(TransactionStatus status) throws TransactionException; } }
从这里可知具体的具体的事务管理机制对Spring来说是透明的,它并不关心那些,那些是对应各个平台需要关心的,所以Spring事务管理的一个优点就是为不同的事务API提供一致的编程模型,如JTA、JDBC、Hibernate、JPA。下面分别介绍各个平台框架实现事务管理的机制。
3.1.1 JDBC事务
如果应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。为了使用DataSourceTransactionManager,你需要使用如下的XML将其装配到应用程序的上下文定义中:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
实际上,DataSourceTransactionManager是通过调用java.sql.Connection来管理事务,而后者是通过DataSource获取到的。通过调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚。
3.1.2 Hibernate事务
如果应用程序的持久化是通过Hibernate实习的,那么你需要使用HibernateTransactionManager。对于Hibernate3,需要在Spring上下文定义中添加如下的<bean>
声明:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
sessionFactory属性需要装配一个Hibernate的session工厂,HibernateTransactionManager的实现细节是它将事务管理的职责委托给org.hibernate.Transaction对象,而后者是从Hibernate Session中获取到的。当事务成功完成时,HibernateTransactionManager将会调用Transaction对象的commit()方法,反之,将会调用rollback()方法。
3.1.3 Java持久化API事务(JPA)
Hibernate多年来一直是事实上的Java持久化标准,但是现在Java持久化API作为真正的Java持久化标准进入大家的视野。如果你计划使用JPA的话,那你需要使用Spring的JpaTransactionManager来处理事务。你需要在Spring中这样配置JpaTransactionManager:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
JpaTransactionManager只需要装配一个JPA实体管理工厂(javax.persistence.EntityManagerFactory接口的任意实现)。JpaTransactionManager将与由工厂所产生的JPA EntityManager合作来构建事务。
3.1.4 Java原生API事务
如果你没有使用以上所述的事务管理,或者是跨越了多个事务管理源(比如两个或者是多个不同的数据源),你就需要使用JtaTransactionManager:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManagerName" value="java:/TransactionManager" /> </bean>
JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成通过UserTransaction.commit()方法提交,事务失败通过UserTransaction.rollback()方法回滚。
3.2 基本事务属性的定义
上面讲到的事务管理器接口PlatformTransactionManager通过getTransaction(TransactionDefinition definition)方法来得到事务,这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。
那么什么是事务属性呢?事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面,如图所示:
而TransactionDefinition接口内容如下:
public interface TransactionDefinition { int getPropagationBehavior(); // 返回事务的传播行为 int getIsolationLevel(); // 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据 int getTimeout(); // 返回事务必须在多少秒内完成 boolean isReadOnly(); // 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的 }
我们可以发现TransactionDefinition正好用来定义事务属性,下面详细介绍一下各个事务属性。
3.2.1 传播行为
见《事务之二:spring事务(事务管理方式,事务5隔离级别,7个事务传播行为,spring事务回滚条件)》
3.2.2 隔离级别
见《事务之二:spring事务(事务管理方式,事务5隔离级别,7个事务传播行为,spring事务回滚条件)》
3.2.3 回滚规则
见《事务之二:spring事务(事务管理方式,事务5隔离级别,7个事务传播行为,spring事务回滚条件)》
3.2.4 超时设置
如@Transactional注解里的参数项timeout:指定事务的超时时长
3.2.5 只读
如@Transactional注解里的参数项readOnly:指定事务是否只读
3.3 TransactionStatus事务状态
若有需要,事务代码可以使用它来检索状态信息,以编程方式请求回滚(而不是抛出导致隐式回滚的异常)
先看源码
// 可以看到它继承自SavepointManager,所以它也会处理还原点 public interface TransactionStatus extends SavepointManager, Flushable { // 判断当前的事务是否是新事务 boolean isNewTransaction(); // 判断该事务里面是否含有还原点~ boolean hasSavepoint(); // Set the transaction rollback-only // 设置了这个,事务的唯一结果是进行回滚。因此如果你在外层给try catche住不让事务回滚,就会抛出你可能常见的异常: // Transaction rolled back because it has been marked as rollback-only void setRollbackOnly(); boolean isRollbackOnly(); //将基础会话刷新到数据存储 for example, all affected Hibernate/JPA sessions @Override void flush(); // Return whether this transaction is completed // 不管是commit或者rollback了都算结束了~~~ boolean isCompleted(); }
Spring中的实现有2个类:
SimpleTransactionStatus类:
public class SimpleTransactionStatus extends AbstractTransactionStatus { private final boolean newTransaction; // 构造函数 public SimpleTransactionStatus() { this(true); } public SimpleTransactionStatus(boolean newTransaction) { this.newTransaction = newTransaction; } @Override public boolean isNewTransaction() { return this.newTransaction; } }
它的实现太简单了,就是标志一下事务是否是新的事务。在Spring内部目前还没有使用场景~
DefaultTransactionStatus类
public class DefaultTransactionStatus extends AbstractTransactionStatus { // 它有很多的标志位,成员变量 @Nullable private final Object transaction; // 是否是新事务 private final boolean newTransaction; // 如果为给定事务打开了新的事务同步 该值为true private final boolean newSynchronization; // 该事务是否标记为了只读 private final boolean readOnly; private final boolean debug; @Nullable private final Object suspendedResources; // 它的唯一构造函数如下: public DefaultTransactionStatus(@Nullable Object transaction, boolean newTransaction, boolean newSynchronization, boolean readOnly, boolean debug, @Nullable Object suspendedResources) { this.transaction = transaction; this.newTransaction = newTransaction; this.newSynchronization = newSynchronization; this.readOnly = readOnly; this.debug = debug; this.suspendedResources = suspendedResources; } // 直接把底层事务返回 public Object getTransaction() { Assert.state(this.transaction != null, "No transaction active"); return this.transaction; } public boolean hasTransaction() { return (this.transaction != null); } // 首先 @Override public boolean isNewTransaction() { return (hasTransaction() && this.newTransaction); } ... // 都由SmartTransactionObject去处理 该接口的实现类有: // JdbcTransactionObjectSupport和JtaTransactionObject(分布式事务) public boolean isGlobalRollbackOnly() { return ((this.transaction instanceof SmartTransactionObject) && ((SmartTransactionObject) this.transaction).isRollbackOnly()); } @Override public void flush() { if (this.transaction instanceof SmartTransactionObject) { ((SmartTransactionObject) this.transaction).flush(); } } }
该DefaultTransactionStatus
是Spring默认使用的事务状态,后面会有很多的接触~
3.4 TransactionInterceptor:事务拦截器
我们已经知道了,它是个MethodInterceptor,被事务拦截的方法最终都会执行到此增强器身上。
MethodInterceptor是个环绕通知,刚好符合我们的开启、提交、回滚事务等操作~
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable { // 构造函数: // 可以不用特殊的指定PlatformTransactionManager 事务管理器,后面会讲解自定义去获取 // 可以自己指定Properties 以及 TransactionAttributeSource public TransactionInterceptor() { } public TransactionInterceptor(PlatformTransactionManager ptm, Properties attributes) { setTransactionManager(ptm); setTransactionAttributes(attributes); } public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) { setTransactionManager(ptm); setTransactionAttributeSource(tas); } // 接下来就是这个invoke方法:就是拦截的入口~ @Override @Nullable public Object invoke(MethodInvocation invocation) throws Throwable { // 获取目标类 Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // invokeWithinTransaction:父类TransactionAspectSupport的模板方法 // invocation::proceed本处执行完成 执行目标方法(当然可能还有其余增强器) return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); } ... }
可以看出,真正做事情的其实还是在父类,它有一个执行事务的模版。
// 通过BeanFactoryAware获取到BeanFactory // InitializingBean的afterPropertiesSet是对Bean做一些验证(经常会借助它这么来校验Bean~~~) public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean { // 这个类不允许实现Serializable接口,请注意~~ // NOTE: This class must not implement Serializable because it serves as base // class for AspectJ aspects (which are not allowed to implement Serializable)! /** * Key to use to store the default transaction manager. */ private static final Object DEFAULT_TRANSACTION_MANAGER_KEY = new Object(); // currentTransactionStatus() 方法依托于它 private static final ThreadLocal<TransactionInfo> transactionInfoHolder = new NamedThreadLocal<>("Current aspect-driven transaction"); //Subclasses can use this to return the current TransactionInfo. // Only subclasses that cannot handle all operations in one method // 注意此方法是个静态方法 并且是protected的 说明只有子类能够调用,外部并不可以~~~ @Nullable protected static TransactionInfo currentTransactionInfo() throws NoTransactionException { return transactionInfoHolder.get(); } // 外部调用此Static方法,可议获取到当前事务的状态 从而甚至可议手动来提交、回滚事务 public static TransactionStatus currentTransactionStatus() throws NoTransactionException { TransactionInfo info = currentTransactionInfo(); if (info == null || info.transactionStatus == null) { throw new NoTransactionException("No transaction aspect-managed TransactionStatus in scope"); } return info.transactionStatus; } //========================================== // 事务管理器的名称(若设置,会根据此名称去找到事务管理器~~~~) @Nullable private String transactionManagerBeanName; @Nullable private PlatformTransactionManager transactionManager; @Nullable private TransactionAttributeSource transactionAttributeSource; @Nullable private BeanFactory beanFactory; // 因为事务管理器可能也会有多个 所以此处做了一个简单的缓存~ private final ConcurrentMap<Object, PlatformTransactionManager> transactionManagerCache = new ConcurrentReferenceHashMap<>(4); public void setTransactionAttributeSource(@Nullable TransactionAttributeSource transactionAttributeSource) { this.transactionAttributeSource = transactionAttributeSource; } // 这步操作发现,若传入的为Properties 内部是实际使用的是NameMatchTransactionAttributeSource 去匹配的 // 备注:若调用了此方法 transactionAttributeSource就会被覆盖的哟 public void setTransactionAttributes(Properties transactionAttributes) { NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource(); tas.setProperties(transactionAttributes); this.transactionAttributeSource = tas; } // 若你有多种匹配策略,这也是支持的 可谓非常强大有木有~~~ public void setTransactionAttributeSources(TransactionAttributeSource... transactionAttributeSources) { this.transactionAttributeSource = new CompositeTransactionAttributeSource(transactionAttributeSources); } ... // 接下来就只剩我们最为核心的处理事务的模版方法了: //protected修饰,不允许其他包和无关类调用 @Nullable protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { // 获取事务属性源~ TransactionAttributeSource tas = getTransactionAttributeSource(); // 获取该方法对应的事务属性(这个特别重要) final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); // 这个厉害了:就是去找到一个合适的事务管理器(具体策略详见方法~~~) final PlatformTransactionManager tm = determineTransactionManager(txAttr); // 拿到目标方法唯一标识(类.方法,如service.UserServiceImpl.save) final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); // 如果txAttr为空或者tm 属于非CallbackPreferringPlatformTransactionManager,执行目标增强 // 在TransactionManager上,CallbackPreferringPlatformTransactionManager实现PlatformTransactionManager接口,暴露出一个方法用于执行事务处理中的回调 if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // 看是否有必要创建一个事务,根据`事务传播行为`,做出相应的判断 TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; try { //回调方法执行,执行目标方法(原有的业务逻辑) retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // 出现异常了,进行回滚(注意:并不是所有异常都会rollback的) // 备注:此处若没有事务属性 会commit 兼容编程式事务吧 completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { //清除信息 cleanupTransactionInfo(txInfo); } // 目标方法完全执行完成后,提交事务~~~ commitTransactionAfterReturning(txInfo); return retVal; } //编程式事务处理(CallbackPreferringPlatformTransactionManager) 会走这里 // 原理也差不太多,这里不做详解~~~~ else { final ThrowableHolder throwableHolder = new ThrowableHolder(); // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> { TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); try { return invocation.proceedWithInvocation(); } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { throw new ThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. throwableHolder.throwable = ex; return null; } } finally { cleanupTransactionInfo(txInfo); } }); // Check result state: It might indicate a Throwable to rethrow. if (throwableHolder.throwable != null) { throw throwableHolder.throwable; } return result; } catch (ThrowableHolderException ex) { throw ex.getCause(); } catch (TransactionSystemException ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); ex2.initApplicationException(throwableHolder.throwable); } throw ex2; } catch (Throwable ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); } throw ex2; } } } // 从容器中找到一个事务管理器 @Nullable protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) { // 如果这两个都没配置,所以肯定是手动设置了PlatformTransactionManager的,那就直接返回即可 if (txAttr == null || this.beanFactory == null) { return getTransactionManager(); } // qualifier 就在此处发挥作用了,他就相当于BeanName String qualifier = txAttr.getQualifier(); if (StringUtils.hasText(qualifier)) { // 根据此名称 以及PlatformTransactionManager.class 去容器内招 return determineQualifiedTransactionManager(this.beanFactory, qualifier); } // 若没有指定qualifier 那再看看是否指定了 transactionManagerBeanName else if (StringUtils.hasText(this.transactionManagerBeanName)) { return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName); } // 若都没指定,那就不管了。直接根据类型去容器里找 getBean(Class) // 此处:若容器内有两个PlatformTransactionManager ,那就铁定会报错啦~~~ else { PlatformTransactionManager defaultTransactionManager = getTransactionManager(); if (defaultTransactionManager == null) { defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY); if (defaultTransactionManager == null) { defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class); this.transactionManagerCache.putIfAbsent( DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager); } } return defaultTransactionManager; } } // ====================================== }
不同的事务处理方式使用不同的逻辑。对于声明式事务的处理与编程式事务的处理,重要区别在于事务属性上,因为编程式的事务处理是不需要有事务属性的
上面处理事务的模版已经分析完成,下面单独摘出来分析一些具体的非常重要的方法。
TransactionAspectSupport.TransactionInfo:内部类 事务Info
它是TransactionAspectSupport的一个protected内部类。我觉得有必要先讲解下它:
protected final class TransactionInfo { // 当前事务 的事务管理器 @Nullable private final PlatformTransactionManager transactionManager; // 当前事务 的事务属性 @Nullable private final TransactionAttribute transactionAttribute; // joinpoint标识 private final String joinpointIdentification; // 当前事务 的TransactionStatus @Nullable private TransactionStatus transactionStatus; // 重点就是这个oldTransactionInfo字段 // 这个字段保存了当前事务所在的`父事务`上下文的引用,构成了一个链,准确的说是一个有向无环图 @Nullable private TransactionInfo oldTransactionInfo; // 唯一的构造函数~~~~ public TransactionInfo(@Nullable PlatformTransactionManager transactionManager, @Nullable TransactionAttribute transactionAttribute, String joinpointIdentification) { this.transactionManager = transactionManager; this.transactionAttribute = transactionAttribute; this.joinpointIdentification = joinpointIdentification; } public PlatformTransactionManager getTransactionManager() { Assert.state(this.transactionManager != null, "No PlatformTransactionManager set"); return this.transactionManager; } // 注意这个方法名,新的一个事务status public void newTransactionStatus(@Nullable TransactionStatus status) { this.transactionStatus = status; } public boolean hasTransaction() { return (this.transactionStatus != null); } //绑定当前正在处理的事务的所有信息到ThreadLocal private void bindToThread() { // Expose current TransactionStatus // 老的事务 先从线程中拿出来,再把新的(也就是当前)绑定进去~~~~~~ this.oldTransactionInfo = transactionInfoHolder.get(); transactionInfoHolder.set(this); } // 当前事务处理完之后,恢复父事务上下文 private void restoreThreadLocalStatus() { transactionInfoHolder.set(this.oldTransactionInfo); } @Override public String toString() { return (this.transactionAttribute != null ? this.transactionAttribute.toString() : "No transaction"); } }
了解了这个Info内部类之后,接下来可议看看TransactionAspectSupport#createTransactionIfNecessary方法:它是创建事务的一个重要方法。
它会判断是否存在事务,根据事务的传播属性。做出不同的处理,也是做了一层包装,核心是通过TransactionStatus来判断事务的属性
创建事务:createTransactionIfNecessary()
// 若有需要 创建一个TransactionInfo (具体的事务从事务管理器里面getTransaction()出来~) protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification) { // If no name specified, apply method identification as transaction name. // 这个简单的说,就是给Name赋值~~~~~ if (txAttr != null && txAttr.getName() == null) { txAttr = new DelegatingTransactionAttribute(txAttr) { @Override public String getName() { return joinpointIdentification; } }; } // 从事务管理器里,通过txAttr拿出来一个TransactionStatus TransactionStatus status = null; if (txAttr != null) { if (tm != null) { status = tm.getTransaction(txAttr); } ... } // 通过TransactionStatus 等,转换成一个通用的TransactionInfo return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); }
准备事务:prepareTransactionInfo()
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, String joinpointIdentification, @Nullable TransactionStatus status) { // 构造一个TransactionInfo TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification); if (txAttr != null) { // 如果已存在不兼容的Tx,事务管理器将标记错误 txInfo.newTransactionStatus(status); } ,.. // We always bind the TransactionInfo to the thread, even if we didn't create // a new transaction here. This guarantees that the TransactionInfo stack // will be managed correctly even if no transaction was created by this aspect. // 这句话是最重要的:把生成的TransactionInfo并绑定到当前线程的ThreadLocal txInfo.bindToThread(); return txInfo; }
提交事务:commitTransactionAfterReturning()
//比较简单 只用用事务管理器提交事务即可~~~ 具体的实现逻辑在事务管理器的commit实现里~~~ protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) { if (txInfo != null && txInfo.getTransactionStatus() != null) { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } }
回滚事务:completeTransactionAfterThrowing()
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) { if (txInfo != null && txInfo.getTransactionStatus() != null) { // 如果有事务属性了,那就调用rollbackOn看看这个异常需不需要回滚 if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } ... } // 编程式事务没有事务属性,那就commit吧 else { try { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } ... } } }
清除(解绑)事务:cleanupTransactionInfo()
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) { if (txInfo != null) { txInfo.restoreThreadLocalStatus(); } }
PlatformTransactionManager:事务管理器
另外一大块内容:事务管理器。谈到事务,肯定是离不开它的。
关于事务管理器,不管是JPA(JpaTransactionManager )还是JDBC(DataSourceTransactionManager)甚至是JTA(JtaTransactionManager)等都实现自接口 PlatformTransactionManager
它只提供三个方法:
public interface PlatformTransactionManager { TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; }
这三个方法就不用再多说。
下面以最常用的实现:DataSourceTransactionManager 做源码分析~~~
先看看它的抽象实现:AbstractPlatformTransactionManager,这个抽象类提供了处理的模版(其实Spring的设计模式中,很多抽象类都提供了实现模版),然后提供开口给子类去各自实现~
AbstractPlatformTransactionManager
可见它是对PlatformTransactionManager的一个抽象实现。实现Spring的标准事务工作流
这个基类提供了以下工作流程处理:
- 确定如果有现有的事务;
- 应用适当的传播行为;
- 如果有必要暂停和恢复事务;
- 提交时检查rollback-only标记;
- 应用适当的修改当回滚(实际回滚或设置rollback-only);
- 触发同步回调注册(如果事务同步是激活的)
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable { //始终激活事务同步(请参阅事务的传播属性~) public static final int SYNCHRONIZATION_ALWAYS = 0; //仅对实际事务(即,不针对由传播导致的空事务)激活事务同步\不支持现有后端事务 public static final int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION = 1; //永远不激活事务同步 public static final int SYNCHRONIZATION_NEVER = 2; // 相当于把本类的所有的public static final的变量都收集到此处~~~~ private static final Constants constants = new Constants(AbstractPlatformTransactionManager.class); // ===========默认值 private int transactionSynchronization = SYNCHRONIZATION_ALWAYS; // 事务默认的超时时间 为-1表示不超时 private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT; //Set whether nested transactions are allowed. Default is "false". private boolean nestedTransactionAllowed = false; // Set whether existing transactions should be validated before participating(参与、加入) private boolean validateExistingTransaction = false; //设置是否仅在参与事务`失败后`将 现有事务`全局`标记为回滚 默认值是true 需要注意~~~ // 表示只要你的事务失败了,就标记此事务为rollback-only 表示它只能给与回滚 而不能再commit或者正常结束了 // 这个调用者经常会犯的一个错误就是:上层事务service抛出异常了,自己把它给try住,并且并且还不throw,那就肯定会报错的: // 报错信息:Transaction rolled back because it has been marked as rollback-only // 当然喽,这个属性强制不建议设置为false~~~~~~ private boolean globalRollbackOnParticipationFailure = true; // 如果事务被全局标记为仅回滚,则设置是否及早失败~~~~ private boolean failEarlyOnGlobalRollbackOnly = false; // 设置在@code docommit调用失败时是否应执行@code dorollback 通常不需要,因此应避免 private boolean rollbackOnCommitFailure = false; // 此处我们直接可以通过属性们来社会,语意思更清晰些了 // 我们发现使用起来有点枚举的意思了,特别是用XML配置的时候 非常像枚举的使用~~~~~~~ // 这也是Constants的重要意义~~~~ public final void setTransactionSynchronizationName(String constantName) { setTransactionSynchronization(constants.asNumber(constantName).intValue()); } public final void setTransactionSynchronization(int transactionSynchronization) { this.transactionSynchronization = transactionSynchronization; } //... 省略上面所有字段的一些get/set方法~~~ // 最为重要的一个方法,根据实物定义,获取到一个事务TransactionStatus @Override public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException { //doGetTransaction()方法是抽象方法,具体的实现由具体的事务处理器提供(下面会以DataSourceTransactionManager为例子) Object transaction = doGetTransaction(); //如果没有配置事务属性,则使用默认的事务属性 if (definition == null) { definition = new DefaultTransactionDefinition(); } //检查当前线程是否存在事务 isExistingTransaction此方法默认返回false 但子类都复写了此方法 if (isExistingTransaction(transaction)) { // handleExistingTransaction方法为处理已经存在事务的情况 // 这个方法的实现也很复杂,总之还是对一些传播属性进行解析,各种情况的考虑~~~~~ 如果有新事务产生 doBegin()就会被调用~~~~ return handleExistingTransaction(definition, transaction, debugEnabled); } // 超时时间的简单校验~~~~ if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); } // 处理事务属性中配置的事务传播特性============== // PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'"); } //如果事务传播特性为required、required_new或nested else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { // 挂起,但是doSuspend()由子类去实现~~~ // 挂起操作,触发相关的挂起注册的事件,把当前线程事物的所有属性都封装好,放到一个SuspendedResourcesHolder // 然后清空清空一下`当前线程事务` SuspendedResourcesHolder suspendedResources = suspend(null); // 此处,开始创建事务~~~~~ try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); // //创建一个新的事务状态 就是new DefaultTransactionStatus() 把个属性都赋值上 DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); // 开始事务,抽象方法,由子类去实现~ doBegin(transaction, definition); //初始化和同步事务状态 是TransactionSynchronizationManager这个类 它内部维护了很多的ThreadLocal prepareSynchronization(status, definition); return status; } catch (RuntimeException | Error ex) { //重新开始 doResume由子类去实现 resume(null, suspendedResources); throw ex; } } // 走到这里 传播属性就是不需要事务的 那就直接创建一个 else { boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); // 这个方法相当于先newTransactionStatus,再prepareSynchronization这两步~~~ // 显然和上面的区别是:中间不回插入调用doBegin()方法,因为没有事务 begin个啥~~ return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); } } // 再看看commit方法 @Override public final void commit(TransactionStatus status) throws TransactionException { //如果是一个已经完成的事物,不可重复提交 if (status.isCompleted()) { throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction"); } DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; // 如果已经标记为了需要回滚,那就执行回滚吧 if (defStatus.isLocalRollbackOnly()) { processRollback(defStatus, false); return; } // shouldCommitOnGlobalRollbackOnly这个默认值是false,目前只有JTA事务复写成true了 // isGlobalRollbackOnly:是否标记为了全局的RollbackOnly if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { processRollback(defStatus, true); return; } // 提交事务 这里面还是挺复杂的,会考虑到还原点、新事务、事务是否是rollback-only之类的~~ processCommit(defStatus); } // rollback方法 里面doRollback方法交给子类去实现~~~ @Override public final void rollback(TransactionStatus status) throws TransactionException { DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; processRollback(defStatus, false); } }
从这个抽象类源码分析可以看出,它绝对是一个非常非常典型的模版实现,各个方法实现都是这样。自己先提供实现模版,很多具体的实现方案都开放给子类,比如begin,suspend, resume, commit, rollback等,相当于留好了众多的连接点
这个类的抽象程度非常的高,逻辑也非常的复杂。要想绝对的理解到位,必须要对JDBC的事务非常了解,而且还对这些代码逻辑必须进行精读。书读百遍,其义自见
DataSourceTransactionManager
本文就以最为常用DataSourceTransactionManager作为实现类,简单的看看实现类具体做了什么~
/
// 它还实现了ResourceTransactionManager接口,提供了getResourceFactory()方法 public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean { // 显然它管理的就是DataSource 而JTA分布式事务管理可能就是各种各样的数据源了 @Nullable private DataSource dataSource; // 不要强制标记为ReadOnly private boolean enforceReadOnly = false; // JDBC默认是允许内嵌的事务的 public DataSourceTransactionManager() { setNestedTransactionAllowed(true); } public DataSourceTransactionManager(DataSource dataSource) { this(); setDataSource(dataSource); // 它自己的InitializingBean也是做了一个简单的校验而已~~~ afterPropertiesSet(); } // 手动设置数据源 public void setDataSource(@Nullable DataSource dataSource) { // 这步处理有必要 // TransactionAwareDataSourceProxy是对dataSource 的包装 if (dataSource instanceof TransactionAwareDataSourceProxy) { this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource(); } else { this.dataSource = dataSource; } } //Return the JDBC DataSource @Nullable public DataSource getDataSource() { return this.dataSource; } // @since 5.0 Spring5.0提供的方法 其实还是调用的getDataSource() 判空了而已 protected DataSource obtainDataSource() { DataSource dataSource = getDataSource(); Assert.state(dataSource != null, "No DataSource set"); return dataSource; } // 直接返回的数据源~~~~ @Override public Object getResourceFactory() { return obtainDataSource(); } ... // 这里返回的是一个`DataSourceTransactionObject` // 它是一个`JdbcTransactionObjectSupport`,所以它是SavepointManager、实现了SmartTransactionObject接口 @Override protected Object doGetTransaction() { DataSourceTransactionObject txObject = new DataSourceTransactionObject(); txObject.setSavepointAllowed(isNestedTransactionAllowed()); // 这个获取有意思~~~~相当于按照线程来的~~~ ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource()); txObject.setConnectionHolder(conHolder, false); return txObject; } // 检查当前事务是否active @Override protected boolean isExistingTransaction(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive()); } // 这是一个核心内容了,里面逻辑需要分析分析~~~ @Override protected void doBegin(Object transaction, TransactionDefinition definition) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; Connection con = null; try { if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { // 从DataSource里获取一个连接(这个DataSource一般是有连接池的~~~) Connection newCon = obtainDataSource().getConnection(); // 把这个链接用ConnectionHolder包装一下~~~ txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } txObject.getConnectionHolder().setSynchronizedWithTransaction(true); con = txObject.getConnectionHolder().getConnection(); // 设置isReadOnly、设置隔离界别等~ Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); // 这里非常的关键,先看看Connection 是否是自动提交的 // 如果是 就con.setAutoCommit(false) 要不然数据库默认没执行一条SQL都是一个事务,就没法进行事务的管理了 if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); con.setAutoCommit(false); } // ====因此从这后面,通过此Connection执行的所有SQL语句只要没有commit就都不会提交给数据库的===== // 这个方法特别特别有意思 它自己`Statement stmt = con.createStatement()`拿到一个Statement // 然后执行了一句SQL:`stmt.executeUpdate("SET TRANSACTION READ ONLY");` // 所以,所以:如果你仅仅只是查询。把事务的属性设置为readonly=true Spring对帮你对SQl进行优化的 // 需要注意的是:readonly=true 后,只能读,不能进行dml操作)(只能看到设置事物前数据的变化,看不到设置事物后数据的改变) prepareTransactionalConnection(con, definition); txObject.getConnectionHolder().setTransactionActive(true); int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } // Bind the connection holder to the thread. // 这一步:就是把当前的链接 和当前的线程进行绑定~~~~ if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } } catch (Throwable ex) { // 如果是新创建的链接,那就释放~~~~ if (txObject.isNewConnectionHolder()) { DataSourceUtils.releaseConnection(con, obtainDataSource()); txObject.setConnectionHolder(null, false); } throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } } // 真正提交事务 @Override protected void doCommit(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); // 拿到链接 然后直接就commit了 Connection con = txObject.getConnectionHolder().getConnection(); try { con.commit(); } catch (SQLException ex) { throw new TransactionSystemException("Could not commit JDBC transaction", ex); } } //doRollback()方法也类似 这里不再细说 }
事务属性readonly=true 后,只能读,不能进行dml操作)(只能看到设置事物前数据的变化,看不到设置事物后数据的改变) 但是但是但是通过源码我发现,你光@Transactional(readOnly = true)这样是不够的,还必须在配置DataSourceTransactionManager的时候,来这么一句dataSourceTransactionManager.setEnforceReadOnly(true),最终才会对你的只读事务进行优化~~~~
难道这是Spring的坑???
其实不是,其实如果仅仅只是来了这么一句@Transactional(readOnly = true)而已,最终会把这个Connection设置为只读:con.setReadOnly(true); 它表示将此**连接**设置为只读模式,作为驱动程序启用数据库优化的提示。 将链接设置为只读模式通知数据库后,数据库会对做自己的只读优化。
但是但是但是,这对数据库而言不一定对于数据库而言这就是readonly事务,这点是非常重要的。(因为毕竟一个事务内可能有多个链接~~~~)
因此若想它变成只读性事务,进行最大程度上的优化,那么请你配置上的时候加上这一句:
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource); dataSourceTransactionManager.setEnforceReadOnly(true); // 让事务管理器进行只读事务层面上的优化 建议开启 return dataSourceTransactionManager; }
基于这个特性,我在我的工作中强烈建议Controller层、Service层甚至Dao层各单元都进行读写分离,这样对读这一层能进行很好的统一优化,提升统一管控的效率
附:JDBC几个重要的API
- 关闭自动提交:java.sql.Connection.setAutoCommit(false) 若是true,每次操作都被认为是一次提交
- 手动提交事务:con.commit();
- 出现异常时回滚,不一定在catch语句中,只要在con.commit()前需要回滚时执行都可:con.rollback();
- 关闭连接:con.close();
- 设置事务隔离级别: java.sql.Connection#setTransactionIsolation()
参考:https://blog.csdn.net/f641385712/article/details/89673753