Spring事务传播机制小记

来源:互联网 发布:粮食行业数据体系现状 编辑:程序博客网 时间:2024/06/10 19:21

前言

之前对spring的事务传播机制没有概念,花点时间去看了事务的源码,以及这些事务传播机制使用的文档,在此做一下简单的笔记

正文

下面说提到的共享事务的意思就是几个service共用同一个事务,如传播机制Propagation.REQUIRED

从源码看AOP如何实现事务

我们想使用事务,那就得配置spring元数据,配置事务管理器以及aop的事务的切面,当然可以在spring的xml配置文件中配置,也可以使用注解,其结果是一样的。
在aop的切面中,配置了切点,IOC在读取元数据信息,进而装配,最后将元数据实例化缓存到IOC容器的过程中,根据配置的切面,符合切点的所有的类,将会代理创建实例对象,将它缓存到IOC容器中。打个比方,事务的织入一般应用在service层,那么我们获取IOC中的某个service对象,其实已经是动态代理创建的对象。
具体请看文章《spring的IOC与AOP读源码小记》第二部分

这里面userService与logonService就是动态代理创建的对象

@Transactional(propagation=Propagation.REQUIRES_NEW)    public void register(RegisterDTO dto) {        userService.addUser(dto);        logonService.addLogon(dto);}

上面的代码,如logonService.addLogon执行的时候到底做了些什么?

在logonService.addLogon这个代码上加入了一个断点,我们F5进去,发现进入了invoke方法中,这说明了是使用了jdk动态代理创建的对象。

在invoke中执行了下面关键的操作
获取事务的拦截器链,这里也就是获取了为TransactionInterceptor的增强

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

转移到ReflectiveMethodInvocation这个类中处理。也就是需要依赖这个类的功能协助完成事务织入

// We need to create a method invocation...                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);                // Proceed to the joinpoint through the interceptor chain.                retVal = invocation.proceed();

在proceed中,很有意思,我觉得这里就像是一个递归,先执行TransactionInterceptor事务的织入,在执行原目标的方法得到结果
递归的结构如: A -> B -> A 拦截器链执行完了,就执行原目标方法跳出递归

先执行TransactionIntercetor事务织入,看看TransactionInterceptor是如何处理吧

public Object invoke(final 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);        // Adapt to TransactionAspectSupport's invokeWithinTransaction...        return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {            @Override            public Object proceedWithInvocation() throws Throwable {                return invocation.proceed();            }        });}

这里invocation.proceed() 回调函数就是跳回到ReflectiveMethodInvocation中执行,由于事务拦截器只有一个,所以执行的是所代理的类的被调用的方法,返回结果

invokeWithinTransaction是织入事务,有三个比较重要的处理方法

开启事务

TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

执行被代理的service的调用的方法。下面方法就是执行上面所说的invocation.proceed,回去执行service调用的方法

retVal = invocation.proceedWithInvocation();

如果出现异常,那么在这个方法中回滚,这个方法默认如果是检查型异常则提交,如果RuntimeException 和 error则回滚

completeTransactionAfterThrowing(txInfo, ex);

这里执行了代码的提交

commitTransactionAfterReturning(txInfo);

下面主要是从这三个方法中去讲述

从源码中看传播机制如何创建事务

开启事务

TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

在里面有一个getTransaction方法

1、某一个service执行的时候,发现当前已经存在一个事务了,那么它执行下面的代码逻辑,根据传播机制选择在handleExistingTransaction中处理,是否要重新创建事务,还是使用原来的事务

if (isExistingTransaction(transaction)) {            // Existing transaction found -> check propagation behavior to find out how to behave.            return handleExistingTransaction(definition, transaction, debugEnabled);}

我们看看handleExistingTransaction如何处理的
①、PROPAGATION_NEVER,非事务运行,存在事务就报错

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {            throw new IllegalTransactionStateException(                    "Existing transaction found for transaction marked with propagation 'never'");}

②、PROPAGATION_NOT_SUPPORTED,非事务运行,如果有事务则挂起

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {            if (debugEnabled) {                logger.debug("Suspending current transaction");            }            Object suspendedResources = suspend(transaction);            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);            return prepareTransactionStatus(                    definition, null, false, newSynchronization, debugEnabled, suspendedResources);}

③、PROPAGATION_REQUIRES_NEW,如果当前有事务则挂起,创建新的事务,没有事务则创建新事务。

代码就不贴了,太长了,反正handleExistingTransaction处理的就是存在事务了,这些传播机制是如何处理的。

2、如果没有存在事务,那么传播机制为PROPAGATION_MANDATORY,抛出异常,如果是PROPAGATION_REQUIRED这些就创建新事务

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {            throw new IllegalTransactionStateException(                    "No existing transaction found for transaction marked with propagation 'mandatory'");        }        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {    doBegin(transaction, definition);}

doBegin是HibernateTransactionManager的方法,它去开启一个事物

从源码看多个事务回滚

completeTransactionAfterThrowing(txInfo, ex)这个方法中去回滚

1、在这个方法里面有txInfo.transactionAttribute.rollbackOn(ex)判断,如果是RuntimeException和Error的异常,则回滚

txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());

这个rollback对于不同的传播机制,处理不同

有savepoint就回滚到保存的回滚点,这个是NESTED的传播机制处理

if (status.hasSavepoint()) {                    if (status.isDebug()) {                        logger.debug("Rolling back transaction to savepoint");                    }                    status.rollbackToHeldSavepoint();                }

如果这个status是新事务,则滚回。每一个service调用的方法,织入事务,都有一个状态。如果是共享事物的service,除了是事务创建最外层的service,它的isNewTransaction是true,其它所共享事务的service的status状态的isNewTransaction为false

else if (status.isNewTransaction()) {                    if (status.isDebug()) {                        logger.debug("Initiating transaction rollback");                    }                    doRollback(status);                }

打个比方

@Transactional(propagation=Propagation.REQUIRES_NEW)    public void register(RegisterDTO dto) {        userService.addUser(dto);        logonService.addLogon(dto);}

register方法是创建一个新的事物,它是最外层的事物边界,userService与logonService的传播机制为REQUIRED,它们相同register创建的事物,它们的status的isNewTransaction为false,
从上面看,只有到了register才真正的回滚

那么userService与logonService需要回滚做什么呢?

存在事务,说明是共享事物了,那么就标记rollback-only,到最外层的时候一起回滚,如userService和logonService只是表示成rollback-only,抛出的异常在register的事务织入中捕捉,并真正在register中回滚,它的isNewTransaction为true

else if (status.hasTransaction()) {                    if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {                        if (status.isDebug()) {                            logger.debug("Participating transaction failed - marking existing transaction as rollback-only");                        }                        doSetRollbackOnly(status);                    }                    else {                        if (status.isDebug()) {                            logger.debug("Participating transaction failed - letting transaction originator decide on rollback");                        }                    }                }

注意的地方:从上面看共享事物,不是最外层的边界的service,其他的标记成rollback-only,这就要求这些service所抛出的异常,不能try catch 捕获,不然的话,在register,发现没有从这些共享service抛出异常,直接提交,从而抛出rollback-only的异常
具体请看文章《Spring事务异常rollback-only》

2、如果是检查型异常则提交
想检查型异常,出错了,被捕获,但是不会去回滚,直接提交了。事务注解里面或者配置文件里面可以对这些异常做处理从而处理回滚
如果事务

最后说一下,不管是什么事务传播机制,这些service嵌套了以后,最外层的service总能捕获某一个service抛出的RuntimeException异常,从而回滚。比如register中嵌套了userService和logonService,
不过userService和logonService是什么传播机制类型,里面抛出的运行时异常,总能在register事务织入catch到的,从而也一起回滚了
。logonService 是requires_new 那么里面抛出的异常就在register中try掉,让userService的处理能够顺利完成。但是try catch使用也要小心,如果是共享事务的service,try-catch,那么就会出现rollback-only异常。这些都是看源码明白的,实战是不是这样处理我还真不知道,没用过

从源码看多个事务的提交

commitTransactionAfterReturning,这里是提交,在这个方法中通过下面代码处理的

txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());

里面commit代码

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()) {            if (defStatus.isDebug()) {                logger.debug("Transactional code has requested rollback");            }            processRollback(defStatus);            return;        }        if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {            if (defStatus.isDebug()) {                logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");            }            processRollback(defStatus);            // Throw UnexpectedRollbackException only at outermost transaction boundary            // or if explicitly asked to.            if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {                throw new UnexpectedRollbackException(                        "Transaction rolled back because it has been marked as rollback-only");            }            return;        }        processCommit(defStatus);    }

下面这段代码如果发现是全局rollback-only标识在,那么说明之前共享事物某个service已经出现异常了,判断是不是该共享事物的最外层service,如果是,rollback回滚,并抛出rollback-only,没错,抛出异常,为什么呢,因为rollback-only为true,最外层不应该在commitTransactionAfterReturning处理的,而是completeTransactionAfterThrowing处理

如果不是最外层,那么继续标记成rollback-only,在最外层以后再做处理,我看最外层还是抛rollback-only,因为之前共享事物,已经有rollback-only为true了,那么为什么会在commitTransactionAfterReturning处理,我看是在register代码中就try cacth掉那个异常,从而没有捕获,进入completeTransactionAfterThrowing处理异常

if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {

processCommit方法中处理
从代码上看,对有savepoint的处理

if (status.hasSavepoint()) {                    if (status.isDebug()) {                        logger.debug("Releasing transaction savepoint");                    }                    status.releaseHeldSavepoint();                }

对当前有isNewTransaction为true的处理,直接提交,如果是共享事务最外层边界,直接提交

else if (status.isNewTransaction()) {                    if (status.isDebug()) {                        logger.debug("Initiating transaction commit");                    }                    doCommit(status);                }

如果是共享事务,不是最外层,提交跳过了,不会提交,在最外层边界的service执行事务的时候才提交

事务传播机制实例

具体的请看文章,第二篇文章比较好,将多个事务,用jdbc的形式表现出来了,也就是我上面分析,它整体的用jdbc代码表现出来,更容易理解
《spring事务传播机制实例讲解》
《详解spring事务属性》

1 0
原创粉丝点击