Spring的事务机制提供两种应用方式:声明式事务和编程事务。声明式事务包括xml配置文件和@Transactional注解。在实际项目开发中用注解是最多的,所以这里也以声明式事务为例讲解。不过两种事务的底层逻辑大多一致。
解答两个大问题前,先了解xml中如何让spring开启事务管理机制。
xml配置和SpringBoot自动装配
xml配置
<tx:annotation-driven/>是开启spring事务开关。其中annotation-driven是自定义标签,Spring是用AnnotationDrivenBeanDefinitionParser解析器解析遇到的annotation-driven标签。其中解析<tx:annotation-driven/>时会注册BeanFactoryTransactionAttributeSourceAdvisor增强器到IOC容器。这个增强器是专门处理@Transactional注解的事务增强器。后续在bean实例化后并依赖注入后,应用AnnotationAwareAspectJAutoProxyCreator处理器时,对带事务方法的bean应用这个事务增强器
SpringBoot自动装配
如果是Springboot则是依靠@EnalbeTransactionManagement注解自动装配ProxyTransactionManagementConfiguration配置类,这个配置类会注册BeanFactoryTransactionAttributeSourceAdvisor事务增强器。后面的对目标对象进行事务增强就和xml的过程一样。
如果看到上面有点懵,可能缺乏前置知识,可先了解AOP动态代理是如何实现的:
Spring AOP原理 收集切面和实例化增强器_Alan CGH的博客-优快云博客
Spring AOP原理 应用增强创建代理_Alan CGH的博客-优快云博客
1.在哪里生成代理对象,什么时候做的?
上面提到是感知实例化的自动代理创建器对bean做后置处理时找到可应用在bean身上的增强器,因为此时的bean是带@Transactional注解的,无论在类上、类的方法中、接口上、接口方法中都会被TransactionAttributeSourceAdvisor的TransactionAttributeSourcePointcut匹配到。
下面会以项目中的一个带有@Transactional注解的Service类解析
首先orderServiceImpl已经被实例化并进行了依赖注入。现在进入bean加载流程的最后一步初始化bean。
在对bean应用后置处理器的时候,会衔接给自动代理创建器。这里是IOC bean加载和事务代理对象的入口。不同的切面代理会有不同的入口,比如@AspectJ切面类的增强方法,可能是在doCreateBean()加载之前的resolveBeanBeforeInitlization()时就被代理了。
在AutoProxyCreator.wrapIfNeccessary()中,找到了能够匹配orderServiceImpl的TransactionAttributeAdvisor增强器。因为有了增强器所以下一步必须创建代理,将事务增强器注入到代理对象中,返回代理对象代替真实的orderServiceImpl对象放入IOC容器。
总结:
在AutoProxyCreator对实例化且依赖注入后的bean做后处理时,找到匹配bean的增强器,所以对bean做代理。这其中的增强器就是事务增强器TransactionAttributeSourceAdvisor。
2.代理对象的事务方法如何生效的,涉及开启事务,回滚,提交
TransactionAttributeSourceAdvisor本身只是一个增强器要让事务方法生效它依靠TransactionInterceptor是一个方法拦截器。增强器持有拦截器的引用。事务方法的运行机制就在拦截器的invoke()中。
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// 1.1 调用TransactionAspectSupport
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
1.1 事务代理的支持对象。让当前的目标方法在事务中执行。
一开始说到Spring支持两种事务方式,这里我们只看声明式事务。声明式事务判断依据是方法上是否有事务属性。
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 TransactionManager tm = determineTransactionManager(txAttr);
// 省略响应式事务管理器代码 ...
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 事务属性非空,则执行声明式事务逻辑
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 2.1 根据事务属性的参数创建事务信息
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// 继续执行增强器链,最后会执行目标方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 2.2 执行过程中发生异常,看是否需要回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 每次事务方法增强后,清理一些资源
cleanupTransactionInfo(txInfo);
}
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 2.3 看是否需要提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// 编程式事务的处理逻辑
}
}
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition/*用户定义的事务属性*/)
throws TransactionException {
// 取用户定义的事务属性或者全局的默认事务属性,作为本次事务的配置参数
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 看当前线程的LocalMap中是否有JDBC的connection,如果没有在下面startTransaction()中会从数据源获取新链接并绑定到线程中
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
// 如果connection非空且connection的事务状态是actived的,代表当前线程存在活动中的事务了
if (isExistingTransaction(transaction)) {
// 根据本次事务的配置参数校验已存在事务的传播策略,抛出异常 或 创建保存点 或 创建新事务
return handleExistingTransaction(def, transaction, debugEnabled);
}
// 本次事务参数不支持事务,直接抛异常通知用户
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
// 支持事务、新事务、嵌套事务
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 嵌套事务 和 建新事务 在上面handleExistingTransaction()已处理,这里理论只有required场景
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
// 3.1 开启事务
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
// 创建新事务失败了,如果之前有旧事务要把它恢复继续执行
resume(null, suspendedResources);
throw ex;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
3.1 当需要创建一个新事务就会走到这个方法,不管是挂起了旧事务然后创建新事务还是头一次创建事务
protected void doBegin(
Object transaction, // 事务对象,里面持有数据库链接对象,可能为空需要获取
TransactionDefinition definition // 事务配置参数) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 事务中没有链接,从数据源获取
Connection newCon = obtainDataSource().getConnection();
// 把链接绑定到事务对象中
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
// 根据本次事务的配置参数,设置本次事务的隔离级别
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// 关闭自动提交=开启事务,还要记录下在事务结束后的finally里要恢复链接的自动提交功能
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
con.setAutoCommit(false);
}
// 如果本次事务设置了只读,就发送"SET TRANSACTION READ ONLY",告诉DB本次事务为已读状态
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// 如果这个事务是新的链接,以数据源为key,链接为val,绑定到线程map中
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
// 异常处理
}
}
当线程回到TransactionAspectSupport.invokeWithinTransaction(),执行完2.1 createTransactionIfNecessary()方法。线程当前就持有了transaction对象并且localMap中绑定了transaction关联的数据库链接对象,开了了手动管理事务。下一步就是执行AOP动态代理的拦截器链式调用,直到调用到目标方法。因此目标方法全称调用栈都是在当前事务中执行,但若调用栈中途需要开新事务则需要再通过代理对象走一遍事务拦截器才能开启新事务。
try {
// 继续执行增强器链,最后会执行目标方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 2.2 执行过程中发生异常,看是否需要回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
当执行调用链过程出错,有可能是目标方法抛出的异常,在2.2处捕获后进行异常处理机制。根据事务参数和异常决定是否回滚还是提交。
2.2 异常处理
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
// txInfo和status一般不可能为空
if (txInfo != null && txInfo.getTransactionStatus() != null) {
// 事务参数声明了在这个异常上是要回滚,那直接回滚 rollbackOn默认策略是RuntimeException和Error子类回滚
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 3.2 具体的事务回滚策略逻辑
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// 3.3 事务参数配置了在这个异常上不回滚,那尝试提交,在commit中也会根据status的标志位rollbackOnly
// 或连接对象的rollbackOnly 进一步决定回滚还是提交
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
3.2 具体的回滚策略逻辑
private void processRollback(
DefaultTransactionStatus status, // 事务状态
boolean unexpected // 默认false) {
try {
boolean unexpectedRollback = unexpected;
try {
// 事务同步器回调,完成前的通知
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
// 如果有保存点,回滚到保存点,然后清空当前事务的保存点,保存点功能依赖事务管理器是否支持,事务管理器依赖数据库链接
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
// 此事务是一个新事务,直接拿到事务绑定的链接对象进行回滚
doRollback(status);
}
else {
// 这个分支代表此事务是最外层的一个大事务
if (status.hasTransaction()) {
// 如果事务的某个小事务被标记成回滚或全局被标记成回滚,则回滚
// 全局回滚标记通常是指在大事务中参与的其中一些方法出现异常被spring感知到,就会全局回滚
// 这里的globalRollback默认为true,可通过配置修改
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
// 把当前事务绑定的数据库链接对象标记成回滚,经debug实验有2种结果。1.异常一直抛到最外层的事务,执行回滚
// 2.异常在业务代码被吞了没有抛出,最外层事务在commit最后提交时会检查连接对象的回滚标记,执行回滚且抛出异常
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
// 当前事务执行完毕,1.事务同步器回调,通知 2.线程解绑事务对象,恢复链接自动提交 3.恢复挂起的旧事务
cleanupAfterCompletion(status);
}
}
3.3 事务提交
@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;
// 不清楚localRollbackOnly什么场景下会设置
if (defStatus.isLocalRollbackOnly()) {
processRollback(defStatus, false);
return;
}
// 提交前检查连接对象的回滚标志位,防止异常在调用过程中被业务代码抛弃,shouldCommitOnGlobalRollbackOnly默认false
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
// 执行回滚,然后会抛出异常
processRollback(defStatus, true);
return;
}
// 提交事务,解绑连接
processCommit(defStatus);
}
总结
分析了@Transactional注解的工作原理,从如何被Spring收集到事务方法然后作用于目标方法上的。到事务如何开启,如何感知目标方法异常,如何回滚。
1.Spring先通过xml配置或@EnableTransactionManagement注解向IOC容器注册BeanFactoryTransactionAttributeSourceAdvisor事务增强器。在IOC容器加载bean过程中,对每个bean应用AutoProxyCreator处理器时会传递给事务增强器看看是否可以应用事务代理进行增强。
2.生成代理对象后,在调用方法时会被AOP代理机制拦截。调用拦截器链过程中执行到TransactionInterceptor拦截器,从而进入事务增强过程。在invokeWithinTransaction方法对事务方法做统筹逻辑。根据事务配置参数,决定事务隔离级别、事务传播、回滚的策略。