java

位置:IT落伍者 >> java >> 浏览文章

Spring声明式事务管理源码解读之事务提交


发布日期:2018年12月14日
 
Spring声明式事务管理源码解读之事务提交

其实我的感觉就是事务提交要比事务开始复杂看事务是否提交我们还是要回到TransactionInterceptor类的invoke方法

Java代码

public Object invoke(MethodInvocation invocation) throws Throwable {// Work out the target class: may be null// The TransactionAttributeSource should be passed the target class// as well as the method which may be from an interfaceClass targetClass = (invocationgetThis() != null) ? invocationgetThis()getClass() : null;// Create transaction if necessaryTransactionInfo txInfo = createTransactionIfNecessary(invocationgetMethod() targetClass);Object retVal = null;try {// This is an around advice// Invoke the next interceptor in the chain// This will normally result in a target object being invokedretVal = invocationproceed();}catch (Throwable ex) {// target invocation exceptiondoCloseTransactionAfterThrowing(txInfo ex);throw ex;}finally {doFinally(txInfo);//业务方法出栈后必须先执行的一个方法}doCommitTransactionAfterReturning(txInfo);return retVal;}

其中的doFinally(txInfo)那一行很重要也就是说不管如何这个doFinally方法都是要被调用的为什么它这么重要呢举个例子

我们还是以propregation_required来举例子吧假设情况是这样的AService中有一个方法调用了BService中的这两个方法都处在事务体之中他们的传播途径都是required那么调用开始了AService的方法首先入方法栈并创建了TransactionInfo的实例接着BService的方法入栈又创建了一个TransactionInfo的实例而重点要说明的是TransactionInfo是一个自身关联的内部类第二个方法入栈时会给新创建的TransactionInfo的实例设置一个属性就是TransactionInfo对象中的private TransactionInfo oldTransactionInfo;属性这个属性表明BService方法的创建的TransactionInfo对象是有一个old的transactionInfo对象的这个oldTransactionInfo对象就是AService方法入栈时创建的TransactionInfo对象我们还记得在createTransactionIfNecessary方法里有这样一个方法吧

Java代码

protected TransactionInfo createTransactionIfNecessary(Method method Class targetClass) {

// We always bind the TransactionInfo to the thread even if we didnt create

// a new transaction here This guarantees that the TransactionInfo stack

// will be managed correctly even if no transaction was created by this aspect

txInfobindToThread();

return txInfo;

}

就是这个bindToThread()方法在作怪

private void bindToThread() {

// Expose current TransactionStatus preserving any existing transactionStatus for

// restoration after this transaction is complete

oldTransactionInfo = (TransactionInfo) currentTransactionInfoget();

currentTransactionInfoset(this);

}

如果当前线程中已经有了一个TransactionInfo则拿出来放到新建的transactionInfo对象的oldTransactionInfo属性中然后再把新建的TransactionInfo设置到当前线程中

这里有一个概念要搞清楚就是TransactionInfo对象并不是表明事务状态的对象表明事务状态的对象是TransactionStatus对象这个对象同样是TransactionInfo的一个属性

接下来BService中的那个方法返回那么该它退栈了它退栈后要做的就是doFinally方法即把它的oldTransactionInfo设置到当前线程中(这个TransactionInfo对象显然就是AService方法入栈时创建的怎么现在又要设置到线程中去呢原因就是BService的方法出栈时并不提交事务因为BService的传播途径是required所以要把栈顶的方法所创建transactioninfo给设置到当前线程中)即调用AService的方法时所创建的TransactionInfo对象那么在AServie的方法出栈时同样会设置TransactionInfo对象的oldTransactionInfo到当前线程这时候显然oldTransactionInfo是空的但AService中的方法会提交事务所以它的oldTransactionInfo也应该是空了

在这个小插曲之后接下来就应该是到提交事务了之前在AService的方法出栈时我们拿到了它入栈时创建的TransactionInfo对象这个对象中包含了AService的方法事务状态即TransactionStatus对象很显然太显然了事务提交中的任何属性都和事务开始时的创建的对象息息相关这个TransactionStatus对象哪里来的我们再回头看看createTransactionIfNessary方法吧

Java代码

protected TransactionInfo createTransactionIfNecessary(Method method Class targetClass) {

txInfonewTransactionStatus(thistransactionManagergetTransaction(txAttr));

}

再看看transactionManagergetTransaction(txAttr)方法吧

Java代码

public final TransactionStatus getTransaction(TransactionDefinition definition)

throws TransactionException {

else if (definitiongetPropagationBehavior() == TransactionDefinitionPR

OPAGATION_REQUIRED ||

definitiongetPropagationBehavior() == TransactionDefinitionPROP

AGATION_REQUIRES_NEW ||

definitiongetPropagationBehavior() == TransactionDefinitionPROPAGA

TION_NESTED) {

if (debugEnabled) {

loggerdebug(Creating new transaction with name [ + definition

getName() + ]);

}

doBegin(transaction definition);

boolean newSynchronization = (thistransactionSynchronization != SYN

CHRONIZATION_NEVER);

return newTransactionStatus(definition transaction true newSynchro

nization debugEnabled null);

//注意这里的返回值返回的就是一个TransactionStatus对象这个对象表明了一个事务的状态

如说是否是一个新的事务事务是否已经结束等等这个对象是非常重要的在事务提交的时候还是

会用到它的}

}

}

还有一点需要说明的是AService的方法在执行之前创建的transactionstatus确实是通过这个方法创建的但是BService的方法在执行之前创建transactionstatus的方法就与这个不一样了下面会有详解

回顾了事务开始时所调用的方法之后是不是觉得现在对spring如何处理事务越来越清晰了呢由于这么几个方法的调用每个方法入栈之前它的事务状态就已经被设置好了这个事务状态就是为了在方法出栈时被调用而准备的

让我们再次回到BService中的方法出栈的那个时间段看看spring都做了些什么我们知道后入栈的肯定是先出栈BService中的方法后入栈那它肯定要先出栈了它出栈的时候是要判断是否要提交事务释放资源的让我们来看看TransactionInterceptor的invoke的最后那个方法doCommitTransactionAfterReturning

Java代码

protected void doCommitTransactionAfterReturning(TransactionInfo txInfo) {

if (txInfo != null && txInfohasTransaction()) {

if (loggerisDebugEnabled()) {

loggerdebug(Invoking commit for transaction on + txInfoj

oinpointIdentification());

}

thistrmit(txInfogetTransactionStatus());

//瞧提交事务时用到了表明事务状态的那个TransactionStatus对象了

}

}

看这个方法的名字就知道spring是要在业务方法出栈时提交事务貌似很简单但是事实是这样的吗?我们接着往下看

Java代码

public final void commit(TransactionStatus status) throws TransactionException {

DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;

if (defStatusisCompleted()) {

throw new IllegalTransactionStateException(

Transaction is already completed do not call commit or rollback more than once per transaction);

}

if (defStatusisLocalRollbackOnly()) {

if (defStatusisDebug()) {

loggerdebug(Transactional code has requested rollback);

}

processRollback(defStatus);

return;

}

if (!shouldCommitOnGlobalRollbackOnly() && defStatusisGlobalRollbackOnly()) {

if (defStatusisDebug()) {

loggerdebug(Global transaction is marked as rollbackonly but transactional code requested commit);

}

processRollback(defStatus);

throw new UnexpectedRollbackException(

Transaction has been rolled back because it has been marked as rollbackonly);

}

processCommit(defStatus);

}

上面这段代码就是transactionmanager中的commit但是看上去它又把自己的职责分配给别人了从代码里我们看到如果事务已经结束了就抛异常如果事务是rollbackonly的那么就rollback吧但是按照正常流程我们还是想来看一下事务的提交就是processCommit(status)这个方法吧

Java代码

private void processCommit(DefaultTransactionStatus status) throws TransactionException {

try {

boolean beforeCompletionInvoked = false;

try {

triggerBeforeCommit(status);

triggerBeforeCompletion(status);

beforeCompletionInvoked = true;

if (statushasSavepoint()) {

if (statusisDebug()) {

loggerdebug(Releasing transaction savepoint);

}

statusreleaseHeldSavepoint();

}

else if (statusisNewTransaction()) {//这个判断非常重要下面会详细讲解这个判断的作用

if (statusisDebug()) {

loggerdebug(Initiating transaction commit);

}

boolean globalRollbackOnly = statusisGlobalRollbackOnly();

doCommit(status);

// Throw UnexpectedRollbackException if we have a global rollbackonly

// marker but still didnt get a corresponding exception from commit

`````````````````````

}

我们注意到在判断一个事务是否是新事务之前还有一个statushasSavepoint()的判断我认为这个判断事实上就是嵌套事务的判断即判断这个事务是否是嵌套事务如果不是嵌套事务则再判断它是否是一个新事务下面这段话就非常重要了BService的中的方法是先出栈的也就是说在调用BService之前的创建的那个事务状态对象在这里要先被判断但是由于在调用BService的方法之前已经创建了一个Transaction和Session(假设我们使用的是hibernate这时候在创建第二个TransactionInfo(再强调一下吧TransactionInfo并不是TransactionTransaction是真正的事务对象TransactionInfo只不过是一个辅助类而已用来记录一系列状态的辅助类)的TransactionStatus的时候就会进入下面这个方法(当然在这之前会判断一下当前线程中是否已经有了一个SessionHolder对象不清楚SessionHolder作用的同学请看第一篇文章)

Java代码

private TransactionStatus handleExistingTransaction(

TransactionDefinition definition Object transaction boolean debugEnabled)

throws TransactionException {

if (definitiongetPropagationBehavior() == TransactionDefinitionPROPAGATION_NEVER) {

throw new IllegalTransactionStateException(

Transaction propagation never but existing transaction found);

}

if (definitiongetPropagationBehavior() == TransactionDefinitionPROPAGATION_NOT_SUPPORTED) {

if (debugEnabled) {

loggerdebug(Suspending current transaction);

}

Object suspendedResources = suspend(transaction);

boolean newSynchronization = (thistransactionSynchronization == SYNCHRONIZATION_ALWAYS);

return newTransactionStatus(

definition null false newSynchronization debugEnabled suspendedResources);

}

if (definitiongetPropagationBehavior() == TransactionDefinitionPROPAGATION_REQUIRES_NEW) {

if (debugEnabled) {

loggerdebug(Suspending current transaction creating new transaction with name [ +

definitiongetName() + ]);

}

Object suspendedResources = suspend(transaction);

doBegin(transaction definition);

boolean newSynchronization = (thistransactionSynchronization != SYNCHRONIZATION_NEVER);

return newTransactionStatus(

definition transaction true newSynchronization debugEnabled suspendedResources);

}

if (definitiongetPropagationBehavior() == TransactionDefinitionPROPAGATION_NESTED) {

if (!isNestedTransactionAllowed()) {

throw new NestedTransactionNotSupportedException(

Transaction manager does not allow nested transactions by default +

specify nestedTransactionAllowed property with value true);

}

if (debugEnabled) {

loggerdebug(Creating nested transaction with name [ + definitiongetName() + ]);

}

if (useSavepointForNestedTransaction()) {

// Create savepoint within existing Springmanaged transaction

// through the SavepointManager API implemented by TransactionStatus

// Usually uses JDBC savepoints Never activates Spring synchronization

DefaultTransactionStatus status =

newTransactionStatus(definition transaction false false debugEnabled null);

statuscreateAndHoldSavepoint();

return status;

}

else {

// Nested transaction through nested begin and commit/rollback calls

// Usually only for JTA: Spring synchronization might get activated here

// in case of a preexisting JTA transaction

doBegin(transaction definition);

boolean newSynchronization = (thistransactionSynchronization != SYNCHRONIZATION_NEVER);

return newTransactionStatus(definition transaction true newSynchronization debugEnabled null);

}

}

// Assumably PROPAGATION_SUPPORTS

if (debugEnabled) {

loggerdebug(Participating in existing transaction);

}

boolean newSynchronization = (thistransactionSynchronization != SYNCHRONIZATION_NEVER);

return newTransactionStatus(definition transaction false newSynchronization debugEnabled null);

}

我们看到这个方法其实很明了就是什么样的传播途径就创建什么样的transactionstatus这个方法是在事务开始时被调用的拿到我们之前举的例子中来看下我们就恍然大悟了原来如果之前已经创建过事务那个这个新建的transactionstauts就不应该是属于一个newTransaction了所以第个参数就是false了

也就是说在BService的方法出栈要要执行processcommit但是由于BService的那个TransactionStatus不是一个newTransaction所以它根本不会触发这个动作

Java代码

else if (statusisNewTransaction()) {//这个判断非常重要下面会详细讲解这个判断的作用

if (statusisDebug()) {

loggerdebug(Initiating transaction commit);

}

boolean globalRollbackOnly = statusisGlobalRollbackOnly();

doCommit(status);

}

也就是说在BService的方法出栈后事务是不会提交的这完全符合propragation_required的模型

而在AService的方法出栈后AService的方法所对应的那个TransactionStatus对象的newTransaction属性是为true的即它会触发上面这段代码进行真正的事务提交让我们回想一下AService方法入栈之前创建TransactionStatus对象的情形吧

newTransactionStatus(definition transaction true newSynchronization debu

gEnabled null);

看到第个参数为true没有

那么事务该提交了吧事务的提交我想使用过hibernate的人都知道怎么提交了

txObjectgetSessionHolder()getTransaction(mit();

从当前线程中拿到SessionHolder再拿到开始事务的那个Transaction对象然后再commit事务在没有用spring之前我们经常这么做

               

上一篇:MyEclipse内存不足之JVM内存浅谈

下一篇:spring是如何对Service进行事务管理的