熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java開源技術 >> 正文

Spring聲明式事務管理源碼解讀之事務提交

2022-06-13   來源: Java開源技術 

  其實我的感覺就是事務提交要比事務開始復雜看事務是否提交我們還是要回到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 interface Class targetClass = (invocationgetThis() != null) ? invocationgetThis()getClass() : null; // Create transaction if necessary TransactionInfo 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 invoked retVal = invocationproceed(); } catch (Throwable ex) { // target invocation exception doCloseTransactionAfterThrowing(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之前我們經常這麼做


From:http://tw.wingwit.com/Article/program/Java/ky/201311/28431.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.