1. 程式人生 > >Spring 事務事件監控及實現原理

Spring 事務事件監控及實現原理

來源:https://my.oschina.net/zhangxufeng/blog/1976076

前面我們講到了Spring在進行事務邏輯織入的時候,無論是事務開始,提交或者回滾,都會觸發相應的事務事件。本文首先會使用例項進行講解Spring事務事件是如何使用的,然後會講解這種使用方式的實現原理。

1. 示例

對於事務事件,Spring提供了一個註解@TransactionEventListener,將這個註解標註在某個方法上,那麼就將這個方法宣告為了一個事務事件處理器,而具體的事件型別則是由TransactionalEventListener.phase屬性進行定義的。如下是TransactionalEventListener的宣告:

關於這裡的classes屬性需要說明一下,如果指定了classes屬性,那麼當前監聽方法的引數型別就可以直接使用所釋出的事件的引數型別,如果沒有指定,那麼這裡監聽的引數型別可以使用兩種:ApplicationEvent和PayloadApplicationEvent。對於ApplicationEvent型別的引數,可以通過其getSource()方法獲取釋出的事件引數,只不過其返回值是一個Object型別的,如果想獲取具體的型別還需要進行強轉;

對於PayloadApplicationEvent型別,其可以指定一個泛型引數,該泛型引數必須與釋出的事件的引數型別一致,這樣就可以通過其getPayload()方法獲取事務事件釋出的資料了。關於上述屬性中的TransactionPhase,其可以取如下幾個型別的值:

這裡我們假設資料庫有一個user表,對應的有一個UserService和User的model,用於往該表中插入資料,並且插入動作時使用註解標註目標方法。如下是這幾個類的宣告:

上述程式碼中有一點需要注意的是,對於需要監控事務事件的方法,在目標方法執行的時候需要使用ApplicationEventPublisher釋出相應的事件訊息。如下是對上述訊息進行監控的程式:

這裡對於事件的監控,只需要在監聽方法上新增@TransactionalEventListener註解即可。這裡需要注意的一個問題,在實際使用過程中,對於監聽的事務事件,需要使用其他的引數進行事件的過濾,因為這裡的監聽還是會監聽所有事件引數為User型別的事務,而無論其是哪個位置發出來的。如果需要對事件進行過濾,這裡可以封裝一個UserEvent物件,其內儲存一個類似EventType的屬性和一個User物件,這樣在釋出訊息的時候就可以指定EventType屬性,而在監聽訊息的時候判斷當前方法監聽的事件物件的EventType是否為目標type,如果是,則對其進行處理,否則直接略過。下面是上述程式的

xml檔案配置和驅動程式:

執行上述程式,其執行結果如下:

可以看到,這裡確實成功監聽了目標程式的相關事務行為。

2. 實現原理

關於事務的實現原理,這裡其實是比較簡單的,在前面的文章中,我們講解到,Spring對事務監控的處理邏輯在TransactionSynchronization中,如下是該介面的宣告:

很明顯,這裡的TransactionSynchronization介面只是抽象了一些行為,用於事務事件發生時觸發,這些行為在Spring事務中提供了內在支援,即在相應的事務事件時,其會獲取當前所有註冊的TransactionSynchronization物件,然後呼叫其相應的方法。那麼這裡TransactionSynchronization物件的註冊點對於我們瞭解事務事件觸發有至關重要的作用了。這裡我們首先回到事務標籤的解析處,在前面講解事務標籤解析時,我們講到Spring會註冊一個TransactionalEventListenerFactory型別的bean到Spring容器中,這裡關於標籤的解析讀者可以閱讀本人前面的文章Spring事務用法示例與實現原理。這裡註冊的TransactionalEventListenerFactory實現了EventListenerFactory介面,這個介面的主要作用是先判斷目標方法是否是某個監聽器的型別,然後為目標方法生成一個監聽器,其會在某個bean初始化之後由Spring呼叫其方法用於生成監聽器。如下是該類的實現:

這裡關於事務事件監聽的邏輯其實已經比較清楚了。ApplicationListenerMethodTransactionalAdapter本質上是實現了ApplicationListener介面的,也就是說,其是Spring的一個事件監聽器,這也就是為什麼進行事務處理時需要使用ApplicationEventPublisher.publish()方法釋出一下當前事務的事件。

ApplicationListenerMethodTransactionalAdapter在監聽到釋出的事件之後會生成一個TransactionSynchronization物件,並且將該物件註冊到當前事務邏輯中,如下是監聽事務事件的處理邏輯:

這裡需要說明的是,上述annotation屬性就是在事務監聽方法上解析的TransactionalEventListener註解中配置的屬性。可以看到,對於事務事件的處理,這裡建立了一個TransactionSynchronization物件,其實主要的處理邏輯就是在返回的這個物件中,而createTransactionSynchronization()方法內部只是建立了一個TransactionSynchronizationEventAdapter物件就返回了。這裡我們直接看該物件的原始碼:

可以看到,對於事務事件的處理,最終都是委託給了ApplicationListenerMethodAdapter.processEvent()方法進行的。如下是該方法的原始碼:

對於事務事件的處理,總結而言,就是為每個事務事件監聽方法建立了一個TransactionSynchronizationEventAdapter物件,通過該物件在釋出事務事件的時候,會在當前執行緒中註冊該物件,這樣就可以保證每個執行緒每個監聽器中只會對應一個TransactionSynchronizationEventAdapter物件。在Spring進行事務事件的時候會呼叫該物件對應的監聽方法,從而達到對事務事件進行監聽的目的。

3. 小結

本文首先對事務事件監聽程式的使用方式進行了講解,然後在原始碼層面講解了Spring事務監聽器是如何實現的。在Spring事務監聽器使用過程中,需要注意的是要對當前接收到的事件型別進行判斷,因為不同的事務可能會發布同樣的訊息物件過來。

擴充套件閱讀

微信公眾號:javafirst

掃碼關注免費獲取更多資源