天天看點

Spring 5 中文解析資料存儲篇-事務同步和聲明式事物管理

Spring核心篇章: Spring 5 中文解析之核心篇-IoC容器 Spring 5 中文解析核心篇-IoC容器之依賴關系 Spring 5 中文解析核心篇-IoC容器之Bean作用域 Spring 5 中文解析核心篇-IoC容器之自定義Bean性質 Spring 5 中文解析核心篇-IoC容器之BeanDefinition繼承與容器拓展點 Spring 5 中文解析核心篇-IoC容器之基于注解的容器配置 Spring 5 中文解析核心篇-IoC容器之類路徑掃描群組件管理 Spring 5 中文解析核心篇-IoC容器之JSR330标準注解 Spring 5 中文解析核心篇-IoC容器之基于Java容器配置 Spring 5 中文解析核心篇-IoC容器之Environment抽象 Spring 5 中文解析核心篇-IoC容器之ApplicationContext與BeanFactory Spring 5 中文解析核心篇-IoC容器之Resources Spring 5 中文解析核心篇-IoC容器之資料校驗、資料綁定和類型轉換 Spring 5 中文解析核心篇-IoC容器之SpEL表達式 Spring 5 中文解析核心篇-IoC容器之AOP程式設計(上) ") Spring 5 中文解析核心篇-IoC容器之AOP程式設計(下) Spring 5 中文解析核心篇-IoC容器之Spring AOP API Spring測試篇章: Spring 5 中文解析測試篇-Spring測試 Spring 5 中文解析核心篇-內建測試之概要和內建測試注解 Spring 5 中文解析核心篇-內建測試之TestContext(上) Spring 5 中文解析核心篇-內建測試之TestContext(中) Spring 5 中文解析測試篇-內建測試之TestContext(下) Spring 5 中文解析測試篇-Spring MVC測試架構 Spring 5 中文解析測試篇-WebTestClient Spring存儲篇章: Spring 5 中文解析資料存儲篇-Spring架構的事物支援模型的優勢

[Spring 5 中文解析資料存儲篇-事務同步和聲明式事物管理

](

https://mp.weixin.qq.com/s?__biz=MzA3NDgzODYzNg==&tempkey=MTA3OV91TU8vcGlxSXdvTGNhZ2o0a3p2RXZvSGpJeXNCMmNCUkszbU9OZzVSc09rT19Zejl6b3JCWHZHU0JfN3ZrVDhhbzZUV3BfS2s3aHFEakhPb3V4dXVkMVp4ajFfZllOcnM2N3huU2d1ZUJZZlN6T1lZNVVKWHJjOWRkdEg3Uzg3RmpFRzZXbHMzQ3lFUUEwd1JqTl9JOGZzWGxMYWh6N1lhY05DYnFRfn4%3D&chksm=1f7b0caa280c85bcce8c4ffe9fb21629f683d5d9127116dae91dc9b9cbd2f367a19514fef76f#rd) 完整 電子書位址

1.3 資源與事務同步

現在應該清楚如何建立不同的事務管理器,以及如何将它們連結到需要同步到事務的相關資源(例如,将

DataSourceTransactionManager

耦合到

JDBC

DataSource

,将

HibernateTransactionManager

Hibernate

SessionFactory

,等等)。本節描述應用程式代碼如何(通過使用諸如

JDBC

Hibernate

JPA

之類的持久性API直接或間接)確定正确建立、重用和清理這些資源。本節還讨論如何通過相關的

TransactionManager

觸發事務同步(可選)。

1.3.1 進階同步方法

首選方法是使用Spring的基于模闆的最進階别的持久性內建API,或者将本機

ORM

API與具有事務感覺功能的工廠bean或代理一起使用,以管理本地資源工廠。這些可感覺事務的解決方案在内部處理資源的建立和重用、清理、資源的可選事務同步以及異常映射。是以,使用者資料通路代碼不必解決這些任務,而可以完全專注于非樣闆持久性邏輯(譯者:隻專注自身業務邏輯)。通常,你使用本地

ORM

API或通過

JdbcTemplate

采用模闆方法進行

JDBC

通路。這些解決方案将在本參考文檔的後續部分中詳細介紹。

1.3.2 低級同步方法

諸如

DataSourceUtils

(對于

JDBC

) 、

EntityManagerFactoryUtils

JPA

)、

SessionFactoryUtils

Hibernate

)等類在較低級别存在。當你希望應用程式代碼直接處理本地持久性API的資源類型時,可以使用這些類來確定擷取正确的Spring架構管理的執行個體,(可選)同步事務以及處理過程中發生的異常。正确映射到一緻的API。

例如,對于

JDBC

,你可以使用Spring的

org.springframework.jdbc.datasource.DataSourceUtils

類,而不是使用傳統的

JDBC

方法在

DataSource

上調用

getConnection()

方法:

Connection conn = DataSourceUtils.getConnection(dataSource);

如果現有事務已經有與其同步(連結)的連接配接,則傳回該執行個體。否則,方法調用将觸發建立新連接配接,該連接配接(可選)同步到任何現有事務,并可供該同一事務中的後續重用使用。如前所述,任何

SQLException

都包裝在Spring架構

CannotGetJdbcConnectionException

中,該架構是Spring架構的未經檢查的

DataAccessException

類型的層次結構之一。與從

SQLException

可以輕松獲得的資訊相比,這種方法為你提供的資訊更多,并確定了跨資料庫甚至跨不同持久性技術的可移植性。

這種方法在沒有Spring事務管理的情況下也可以使用(事務同步是可選的),是以無論是否使用Spring進行事務管理,都可以使用它。

當然,一旦使用了Spring的

JDBC

支援、

JPA

支援或

Hibernate

支援,你通常不希望使用

DataSourceUtils

或其他幫助類,因為與直接使用相關的API相比,通過Spring抽象進行工作會更快樂。例如,如果你使用Spring

JdbcTemplate

jdbc.object

包簡化了

JDBC

的使用,則正确的連接配接檢索将在背景進行,并且你無需編寫任何特殊代碼。

1.3.3

TransactionAwareDataSourceProxy

最低級别存在

TransactionAwareDataSourceProxy

類。這是目标

DataSource

的代理,該資料源包裝了目标

DataSource

以增強對Spring關聯事務的了解。在這方面,它類似于Java EE伺服器提供的事務性

JNDI

DataSource

你幾乎永遠不需要或不想使用此類,除非必須調用現有代碼并傳遞标準

JDBC

DataSource

接口實作。在這種情況下,該代碼可能可用,但參與了Spring管理的事務。你可以使用前面提到的進階抽象來編寫新代碼。

1.4 聲明式事物管理
大多數Spring Framework使用者選擇聲明式事務管理。此選項對應用程式代碼的影響最小,是以與非侵入式輕量級容器的理念最一緻。

Spring面向切面的程式設計(

AOP

)使Spring架構的聲明式事務管理成為可能。但是,由于事務切面的代碼随Spring架構發行版一起提供并且可以以樣闆方式使用,是以通常不必了解

AOP

概念即可有效地使用此代碼。

Spring架構的聲明式事務管理與

EJB

CMT

相似,因為你可以指定事務行為(或不存在事務),直至單個方法級别。如有必要,你可以在事務上下文中進行

setRollbackOnly()

調用。兩種類型的事務管理之間的差別是:

  • 與綁定到

    JTA

    EJB

    CMT

    不同,Spring架構的聲明式事務管理可在任何環境中使用。通過調整配置檔案,它可以使用

    JDBC

    JPA

    Hibernate

    來處理

    JTA

    事務或本地事務。
  • 你可以将Spring架構聲明式事務管理應用于任何類,而不僅限于

    EJB

    之類的特殊類。
  • Spring架構提供了聲明式 復原規則 ,此功能EJB沒有。提供了對復原規則的程式設計和聲明性支援。
  • Spring架構允許你使用

    AOP

    自定義事務行為。例如,在事務復原的情況下,你可以插入自定義行為。你還可以添加任意通知以及事務通知。使用

    EJB

    CMT

    ,除非使用

    setRollbackOnly()

    ,否則你不能影響容器的事務管理。
  • Spring架構不像高端應用程式伺服器那樣支援跨遠端調用傳播事務上下文。如果需要此功能,建議你使用

    EJB

    。但是,在使用這種功能之前,請仔細考慮,因為通常情況下,你不希望事務跨遠端調用。

復原規則的概念很重要。它們讓你指定哪些異常(和可抛出對象)應引起自動復原。你可以在配置中而不是在Java代碼中聲明性地指定。是以,盡管你仍然可以在

TransactionStatus

對象上調用

setRollbackOnly()

來復原目前事務,但大多數情況下,你可以指定一個規則,即

MyApplicationException

必須始終導緻復原。此選項的主要優點是業務對象不依賴于事務基礎結構。例如,他們通常不需要導入Spring事務API或其他Spring API。

盡管

EJB

容器的預設行為會在系統異常(通常是運作時異常)時自動復原事務,但是

EJB

CMT

不會在應用程式異常(即

java.rmi.RemoteException

以外的已檢查異常)下自動復原事務。盡管Spring聲明式事務管理的預設行為遵循

EJB

約定(僅針對未檢查的異常會自動復原),但自定義此行為通常很有用。

1.4.1 了解Spring 架構的聲明式事物實作

僅僅告訴你使用

@Transactional

注解對類進行注釋,将

@EnableTransactionManagement

添加到你的配置中是不夠的,并希望了解其全部工作原理。為了提供更深入的了解,本節介紹了與事務相關的問題中Spring架構聲明式事務基礎結構的内部工作原理。

關于Spring架構的聲明式事務支援,最重要的概念是通過AOP代理啟用此支援,并且事務通知由中繼資料(目前基于XML或基于注解)驅動。

AOP

與事務性中繼資料的結合産生了一個

AOP

代理,該代理将

TransactionInterceptor

與适當的

TransactionManager

實作結合使用來驅動方法調用環繞的事務。

Spring AOP在 AOP部分 中介紹。

Spring架構的

TransactionInterceptor

為指令式和響應式程式設計模型提供事務管理。攔截器通過檢查方法傳回類型來檢測所需的事務管理風格。傳回諸如

Publisher

Kotlin Flow

(或它們的子類型)之類的響應式的方法符合響應式事務管理的條件。所有其他傳回類型(包括

void

)都将代碼路徑用于指令式事務管理。

事務管理風格影響需要哪個事務管理器。指令式事務需要

PlatformTransactionManager

,而響應式事務則使用

ReactiveTransactionManager

實作。

下圖顯示了在事務代理上調用方法的概念圖:

Spring 5 中文解析資料存儲篇-事務同步和聲明式事物管理

1.4.2 聲明式事物例子

考慮以下接口及其附帶的實作。本示例使用

Foo

Bar

類作為占位符,以便你可以專注于事務使用而不關注特定的域模型。就本示例而言,

DefaultFooService

類在每個已實作方法的主體中引發

UnsupportedOperationException

執行個體的事實是很好的。該行為使你可以檢視正在建立的事務,然後復原以響應

UnsupportedOperationException

執行個體。以下清單顯示了

FooService

接口:

// the service interface that we want to make transactional

package x.y.service;

public interface FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);

}           

以下示例顯示了上述接口的實作:

package x.y.service;

public class DefaultFooService implements FooService {

    @Override
    public Foo getFoo(String fooName) {
        // ...
    }

    @Override
    public Foo getFoo(String fooName, String barName) {
        // ...
    }

    @Override
    public void insertFoo(Foo foo) {
        // ...
    }

    @Override
    public void updateFoo(Foo foo) {
        // ...
    }
}           

假定

FooService

接口的前兩個方法

getFoo(String)

getFoo(String,String)

必須在具有隻讀語義的事務上下文中運作,而其他方法

insertFoo(Foo)

updateFoo(Foo )

,必須在具有讀寫語義的事務上下文中運作。以下幾節将詳細說明以下配置:

<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 該bean具有事物 -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- 事物通知 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!-- the transactional semantics... -->
        <tx:attributes>
            <!-- 以‘get’開頭的方法具有隻讀事物設定 -->
            <tx:method name="get*" read-only="true"/>
            <!-- 其他使用預設事物設定 -->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!-- 定義事物切面 -->
    <aop:config>
        <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
      <!--使用advisor綁定事物通知與切面-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
    </aop:config>

    <!-- 資料源配置 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
        <property name="username" value="scott"/>
        <property name="password" value="tiger"/>
    </bean>

    <!-- 事物管理 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- other <bean/> definitions here -->

</beans>           

檢查前面的配置。它假定你要使服務對象

fooService

bean成為事務性的。要應用的事務語義封裝在

<tx:advice/>

定義中。

<tx:advice/>

定義為“以

get

開頭的所有方法都将在隻讀事務的上下文中運作,而所有其他方法都将以預設事務語義運作”。

<tx:advice/>

标記的

transaction-manager

屬性設定為要驅動事務的

TransactionManager

bean的名稱(在本例中為

txManager

bean)。

如果要連接配接的

TransactionManager

的bean名稱具有名稱

transactionManager

,則可以在事務通知(

<tx:advice />

)中省略

transaction-manager

屬性。如果要連接配接的

TransactionManager

bean具有其他名稱,則必須顯式使用

transaction-manager

屬性,如上例所示。

<aop:config/>

定義可確定由

txAdvice

bean定義的事務通知在程式的适當位置運作。首先,定義一個切入點,該切入點與

FooService

接口(

fooServiceOperation

)中定義的任何操作的執行相比對。然後,使用

advisor

程式将切入點與

txAdvice

關聯。結果表明,在執行

fooServiceOperation

時,将運作

txAdvice

定義的通知。

<aop:pointcut/>

元素中定義的表達式是

AspectJ

切入點表達式。有關Spring中切入點表達式的更多詳細資訊,請參見

一個普遍的要求是使整個服務層具有事務性。最好的方法是更改切入點表達式以比對服務層中的任何操作。以下示例顯示了如何執行此操作:

<aop:config>
    <aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
</aop:config>           
在前面的示例中,假定你的所有服務接口都在

x.y.service

包中定義。有關更多詳細資訊,請參見

現在我們已經分析了配置,你可能會問自己:“所有這些配置實際上是做什麼的?”

前面顯示的配置用于圍繞從

fooService

bean定義建立的對象建立事務代理。代理配置有事務通知,以便在代理上調用适當的方法時,根據與該方法關聯的事務配置,事務将被啟動、挂起、标記為隻讀等等。

考慮下面的程式,該程式測試驅動前面顯示的配置:

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);
        FooService fooService = (FooService) ctx.getBean("fooService");
        fooService.insertFoo (new Foo());
    }
}           
參考代碼:

org.liyong.dataaccess.starter.DeclarativeTransactionManagerIocContainer

運作上述程式的輸出類似以下内容(為清晰起見,

DefaultFooService

類的

insertFoo(..)

方法抛出的Log4J輸出和

UnsupportedOperationException

的堆棧跟蹤已被截斷):

<!-- the Spring container is starting up... -->
[AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy for bean 'fooService' with 0 common interceptors and 1 specific interceptors

<!-- the DefaultFooService is actually proxied -->
[JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService]

<!-- ... the insertFoo(..) method is now being invoked on the proxy -->
[TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo

<!-- the transactional advice kicks in here... -->
[DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo]
[DataSourceTransactionManager] - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction

<!-- the insertFoo(..) method from DefaultFooService throws an exception... -->
[RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should rollback on java.lang.UnsupportedOperationException
[TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoo due to throwable [java.lang.UnsupportedOperationException]

<!-- and the transaction is rolled back (by default, RuntimeException instances cause rollback) -->
[DataSourceTransactionManager] - Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@a53de4]
[DataSourceTransactionManager] - Releasing JDBC Connection after transaction
[DataSourceUtils] - Returning JDBC Connection to DataSource

Exception in thread "main" java.lang.UnsupportedOperationException at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14)
<!-- AOP infrastructure stack trace elements removed for clarity -->
at $Proxy0.insertFoo(Unknown Source)
at Boot.main(Boot.java:11)           

要使用響應式事務管理,代碼必須使用響應式類型。

Spring架構使用

ReactiveAdapterRegistry

來确定方法傳回類型是否為響應式。

下面的清單顯示了以前使用的

FooService

的修改版本,但是這次代碼使用了響應式類型:

// the reactive service interface that we want to make transactional

package x.y.service;

public interface FooService {

    Flux<Foo> getFoo(String fooName);

    Publisher<Foo> getFoo(String fooName, String barName);

    Mono<Void> insertFoo(Foo foo);

    Mono<Void> updateFoo(Foo foo);

}           

下面例子顯示前面接口的實作:

package x.y.service;

public class DefaultFooService implements FooService {

    @Override
    public Flux<Foo> getFoo(String fooName) {
        // ...
    }

    @Override
    public Publisher<Foo> getFoo(String fooName, String barName) {
        // ...
    }

    @Override
    public Mono<Void> insertFoo(Foo foo) {
        // ...
    }

    @Override
    public Mono<Void> updateFoo(Foo foo) {
        // ...
    }
}           

指令式和響應式事務管理對事務邊界和事務屬性定義共享相同的語義。指令式事務和響應式事務的主要差別在于後者的延遲特性。

TransactionInterceptor

用事務運算符修飾傳回的響應類型,以開始和清理事務。是以,調用事務響應性方法将實際事務管理延遲到激活響應性類型的處理的訂閱類型。

響應式事務管理的另一方面涉及資料轉義,這是程式設計模型的自然結果。

指令式事務的方法傳回值将在方法成功終止時從事務性方法傳回,以便部分計算的結果不會脫離方法閉包。

響應式事務方法傳回一個響應式包裝器類型,該類型表示一個計算序列以及一個開始和完成計算的承諾(譯者:類似Java中的

Future

的概念)。

當事務正在進行但不一定完成時,釋出者可以發出資料。是以,依賴于成功完成整個事務的方法需要確定完成并緩沖調用代碼中的結果。

1.4.3 復原聲明式事物

上一節概述了如何在應用程式中聲明式地指定類(通常是服務層類)的事務性設定的基礎。本節介紹如何以簡單的聲明方式控制事務的復原。

要向Spring架構的事務基礎結構表明事務的工作要復原,推薦的方法是從目前在事務上下文中執行的代碼中抛出異常。Spring 架構的事務基礎結構代碼捕獲了所有未處理的

Exception

,因為它使調用棧冒泡方式,并确定是否将事務标記為復原。

在預設配置中,Spring架構的事務基礎結構代碼僅在運作時未檢查的異常情況下将事務标記為復原。也就是說,當抛出的異常是

RuntimeException

的執行個體或子類時。(預設情況下,

Error

執行個體也會導緻復原)。從事務方法引發的檢查異常不會導緻預設配置中的復原。

你可以準确配置哪些異常類型将事務标記為復原,包括檢查的異常。以下XML代碼段示範了如何為檢查、特定于應用程式的異常類型配置復原:

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
    <tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
    <tx:method name="*"/>
    </tx:attributes>
</tx:advice>           

如果你不希望在引發異常時復原事務,則還可以指定“不復原規則”。以下示例告訴Spring架構的事務基礎結構即使在未處理

InstrumentNotFoundException

也要送出事務:

<tx:advice id="txAdvice">
    <tx:attributes>
    <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
    <tx:method name="*"/>
    </tx:attributes>
</tx:advice>           

當Spring架構的事務基礎結構捕獲到異常并咨詢已配置的復原規則以确定是否将事務标記為復原時,最強的比對規則獲勝。是以,在以下配置的情況下,除

InstrumentNotFoundException

之外的任何異常都将導緻事務的復原:

<tx:advice id="txAdvice">
    <tx:attributes>
    <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
    </tx:attributes>
</tx:advice>           

你還可以通過程式設計方式訓示所需的復原。盡管很簡單,但此過程具有很大的侵入性,并将你的代碼緊密耦合到Spring架構的事務基礎結構。以下示例顯示如何以程式設計方式訓示所需的復原:

public void resolvePosition() {
    try {
        // some business logic...
    } catch (NoProductInStockException ex) {
        // trigger rollback programmatically
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}           

強烈建議你盡可能使用聲明式方法進行復原。如果你絕對需要它,則可以使用程式設計式復原,但是面對實作幹淨的基于POJO的體系結構時,它的用法就不那麼理想了。

org.liyong.dataaccess.starter.RollBackDeclarativeTransactionManagerIocContainer

1.4.4 為不同bean配置不同僚物語義

考慮以下場景:你有許多服務層對象,并且你希望對每個對象應用完全不同的事務配置。你可以通過定義具有不同切入點和

advice-ref

屬性值的不同

<aop:advisor/>

元素來做到這一點。

作為比較點,首先假定所有服務層類都在根

x.y.service

包中定義。要使所有在該包(或子包)中定義的類執行個體化并且名稱以

Service

結尾的bean具有預設的事務配置,可以編寫以下代碼:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

   <!-- 定義切入點和事物通知 -->
    <aop:config>

        <aop:pointcut id="serviceOperation"
                expression="execution(* x.y.service..*Service.*(..))"/>

        <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>

    </aop:config>

    <!-- 在事物中執行 -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>
    <bean id="barService" class="x.y.service.extras.SimpleBarService"/>

    <!-- ... 不在事物中執行 -->
    <bean id="anotherService" class="org.xyz.SomeService"/> <!-- (not in the right package) -->
    <bean id="barManager" class="x.y.service.SimpleBarManager"/> <!-- (doesn't end in 'Service') -->

    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!-- other transaction infrastructure beans such as a TransactionManager omitted... -->

</beans>
           

以下示例顯示如何使用完全不同的事務設定配置兩個不同的Bean:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <aop:config>

        <aop:pointcut id="defaultServiceOperation"
                expression="execution(* x.y.service.*Service.*(..))"/>

        <aop:pointcut id="noTxServiceOperation"
                expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>

        <aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>

        <aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>

    </aop:config>

    <!-- this bean will be transactional (see the 'defaultServiceOperation' pointcut) -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- this bean will also be transactional, but with totally different transactional settings -->
    <bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>

    <tx:advice id="defaultTxAdvice">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <tx:advice id="noTxAdvice">
        <tx:attributes>
            <tx:method name="*" propagation="NEVER"/>
        </tx:attributes>
    </tx:advice>

    <!-- other transaction infrastructure beans such as a TransactionManager omitted... -->

</beans>           

org.liyong.dataaccess.starter.DefferentBeanTransactionManagerIocContainer

1.4.5 配置

本節總結了可以使用

<tx:advice/>

标記指定的各種事務設定。預設的

<tx:advice/>

設定為:

  • 傳播設定

    REQUIRED

  • 隔離級别為

    DEFAULT

  • 事務是讀寫。
  • 事務逾時預設為基礎事務系統的預設逾時,如果不支援逾時,則預設為無。
  • 任何

    RuntimeException

    都會觸發復原,而任何檢查的

    Exception

    都不會觸發。

你可以更改這些預設設定。下表總結了嵌套在

<tx:advice/>

<tx:attributes/>

标簽中的

<tx:method/>

标簽的各種屬性:

屬性 必填 預設值 描述

name

Yes

與事務屬性關聯的方法名稱。通配符(

*

)可用于将相同的事務屬性設定與多種方法關聯(例如,

get*

handle*

on*Event

等)。

propagation

No

REQUIRED

事務傳播行為。

isolation

No

DEFAULT

事務隔離級别。僅适用于

REQUIRED

REQUIRES_NEW

的傳播設定。

timeout

No

-1

事務逾時(秒)。僅适用于

REQUIRED

REQUIRES_NEW

傳播。

read-only

No

false

讀寫與隻讀事務。僅适用于

REQUIRED

REQUIRES_NEW

rollback-for

No

以逗号分隔的觸發復原的

Exception

執行個體清單。例如,

com.foo.MyBusinessException

ServletException

no-rollback-for

No

不觸發復原的

Exception

執行個體的逗号分隔清單。例如,

com.foo.MyBusinessException

ServletException

1.4.6 使用

@Transactional

除了基于XML的聲明式方法進行事務配置外,還可以使用基于注解的方法。直接在Java源代碼中聲明事務語義會使聲明更加接近受影響的代碼。不存在過多耦合的風險,因為原本打算以事務方式使用的代碼幾乎總是以這種方式部署。

還支援使用标準的

javax.transaction.Transactional

注解來替代Spring自己的注解。請參閱

JTA 1.2

文檔以擷取更多詳細資訊。

使用

@Transactional

注解提供的易用性将通過一個示例得到最好的說明,下面的示例對此進行了說明。考慮以下類定義:

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName) {
        // ...
    }

    Foo getFoo(String fooName, String barName) {
        // ...
    }

    void insertFoo(Foo foo) {
        // ...
    }

    void updateFoo(Foo foo) {
        // ...
    }
}           

在上面的類級别使用,注解表示聲明類(及其子類)的所有方法的預設值。另外,每種方法都可以單獨注解。注意,類級别的注解不适用于類層次結構中的祖先類。在這種情況下,需要在本地重新聲明方法,以參與子類級别的注解。

當将一個以上的POJO類在Spring上下文中定義為bean時,可以通過

@Configuration

類中的

@EnableTransactionManagement

注解使bean執行個體具有事務性。有關完整的詳細資訊,請參見

javadoc

在XML配置中,

<tx:annotation-driven/>

标簽提供了類似的便利操作:

<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- this is the service object that we want to make transactional -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="txManager"/><!-- a TransactionManager is still required --> //1

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- (this dependency is defined somewhere else) -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- other <bean/> definitions here -->

</beans>           
  1. 使bean執行個體具有事務性的行為

TransactionManager

transactionManager

,則可以在

<tx:annotation-driven/>

标簽中省略

transaction-manager

屬性。如果要依賴注入的

TransactionManager

bean具有其他名稱,則必須使用

transaction-manager

相對于指令式程式設計,響應式事務方法使用響應式傳回類型,如下清單所示:

// the reactive service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Publisher<Foo> getFoo(String fooName) {
        // ...
    }

    Mono<Foo> getFoo(String fooName, String barName) {
        // ...
    }

    Mono<Void> insertFoo(Foo foo) {
        // ...
    }

    Mono<Void> updateFoo(Foo foo) {
        // ...
    }
}           

請注意,對于傳回的

Publisher

,在響應流取消信号方面有一些特殊注意事項。有關更多詳細資訊,請參見“使用

TransactionOperator

”下的“

取消信号

”部分。

​ 方法可見性和@Transactional

使用代理時,應僅将

@Transactional

注解應用于具有公共可見性的方法。如果使用

@Transactional

注解對

protected

private

或程式包可見的方法進行注解,則不會引發任何錯誤,但是帶注解的方法不會顯示已配置的事務設定。如果需要注解非公共方法,請考慮使用

AspectJ

(稍後描述)。

你可以将

@Transactional

注解應用于接口定義、接口上的方法、類定義或類上的公共方法。但是,僅

@Transactional

注解的存在不足以激活事務行為。

@Transactional

注解僅僅是中繼資料,可以被某些支援

@Transactional

的運作時基礎結構使用,并且可以使用中繼資料來配置具有事務行為的适當Bean。在前面的示例中,

<tx:annotation-driven/>

元素打開事務行為。

Spring團隊推薦僅使用

@Transactional

注解對具體類(以及具體類的方法)進行注解,而不是對接口進行注解。你當然可以在接口(或接口方法)上放置

@Transactional

注解,但這僅在你使用基于接口的代理時才可以達到預期。Java注解不能從接口繼承的事實意味着,如果你使用基于類的代理(

proxy-target-class="true"

)或基于編織的切面(

mode =“aspectj”

),則事務設定不會由代理和編織基礎結構識别,并且該對象未包裝在事務代理中。

在代理模式(預設)下,僅攔截通過代理傳入的外部方法調用。這意味着即使調用的方法标記有

@Transactional

,自調用(實際上是目标對象中的方法調用目标對象的另一種方法)也不會在運作時使用實際事務(譯者:調用執行個體自身的方法就是自調用)。另外,必須完全初始化代理才能提供預期的行為,是以你不應在初始化代碼(即

@PostConstruct

)中依賴此功能。

如果期望自調用也與事務包裝在一起,請考慮使用

AspectJ

模式(請參見下表的

mode

屬性)。在這種情況下,首先沒有代理。而是編織目标類(即,修改其位元組碼)以将

@Transactional

轉換為任何方法上的運作時行為。

XML屬性 注解屬性

transaction-manager

N/A (檢視

Transaction-ManagementConfigurer

-javadoc)

transactionManager

要使用的事務管理器的名稱。如上例所示,僅當事務管理器的名稱不是

transactionManager

時才需要。

mode

mode

proxy

預設模式(代理)通過使用Spring的AOP架構來處理帶注解的bean(遵循代理語義,如前所述,僅适用于通過代理傳入的方法調用)。替代模式(

aspectj

)則将受影響的類與Spring的

AspectJ

事務切面進行編織,修改目标類位元組碼以應用于任何類型的方法調用。

AspectJ

編織需要在類路徑中使用

spring-aspects.jar

并啟用加載時編織(或編譯時編織)。(有關如何設定加載時編織的詳細資訊,請參見 Spring配置 。)

proxy-target-class

proxyTargetClass

false

僅适用于代理模式。控制為使用

@Transactional

注解注釋的類建立哪種類型的事務代理。如果

proxy-target-class

屬性設定為

true

,則将建立基于類的代理。如果

proxy-target-class

false

或省略了屬性,則将建立基于标準JDK接口的代理。(有關不同代理類型的詳細檢查,請參見 代理機制

order

order

Ordered.LOWEST_PRECEDENCE

定義應用于帶

@Transactional

注解的bean的事務通知的順序。(有關AOP通知排序相關規則的更多資訊,請參見 通知順序 。)沒有指定的順序意味着AOP子系統确定通知的順序。
處理

@Transactional

注解的預設通知模式是代理,它僅允許通過代理攔截調用。同一類内的本地調用無法以這種方式被攔截(自調用)。對于更進階的攔截模式,請考慮結合編譯時或加載時編織切換到

Aspectj

模式。

proxy-target-class

屬性控制為使用

@Transactional

proxy-target-class

設定為

true

proxy-target-class

false

或省略了屬性,則将建立基于标準JDK接口的代理。(有關不同代理類型的讨論,請參見 core.html

@EnableTransactionManagement

<tx:annotation-driven/>

僅在定義它們的相同應用程式上下文中的bean上查找

@Transactional

。這意味着,如果将注解驅動的配置放在

DispatcherServlet

WebApplicationContext

中,它将僅在控制器而不是服務中檢查

@Transactional

bean。有關更多資訊,請參見 MVC

在評估方法的事務設定時,最派生的位置優先(譯者:範圍最小的優先級越高,例如:類和方法配置時方法優先級更高)。在下面的示例中,

DefaultFooService

類在類級别使用隻讀事務的設定進行注解,但是同一類中

updateFoo(Foo)

方法上的

@Transactional

注解優先于定義的事務設定在類級别上。

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {

    public Foo getFoo(String fooName) {
        // ...
    }

    // these settings have precedence for this method
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void updateFoo(Foo foo) {
        // ...
    }
}           

@Transactional

設定

@Transactional

注解是中繼資料,它指定接口、類或方法必須具有事務語義(例如,

在調用此方法時啟動一個全新的隻讀事務、暫停任何現有事務

)。預設的

@Transactional

設定如下:

  • 事物傳播設定為

    PROPAGATION_REQUIRED

  • 事物隔離級别為

    ISOLATION_DEFAULT

  • 事務是讀寫的。
  • RuntimeException

    都會觸發復原,而任何檢測的

    Exception

你可以更改這些預設設定。下表總結了

@Transactional

注解的各種屬性:

類型
value

String

可選的限定符,指定要使用的事務管理器。
propagation

enum

:

Propagation

可選的傳播設定。

isolation

enum

Isolation

可選的隔離級别。僅适用于

REQUIRED

REQUIRES_NEW

的傳播值。

timeout

int

(以秒為機關)
可選的事務逾時。僅适用于

REQUIRED

REQUIRES_NEW

readOnly

boolean

REQUIRED

REQUIRES_NEW

的值。

rollbackFor

Class對象數組,必須從

Throwable

派生。
必須引起復原的異常類的可選數組。

rollbackForClassName

類名數組。這些類必須從

Throwable

必須引起復原的異常類名稱的可選數組。

noRollbackFor

Throwable

不能導緻復原的異常類的可選數組。

noRollbackForClassName

字元串類名稱的數組,必須從

Throwable

不能引起復原的異常類名稱的可選數組。

目前,你無法對事務名稱進行顯式控制,其中“名稱”是指顯示在事務螢幕(如果适用)(例如,

WebLogic

的事務螢幕)和日志輸出中的事務名稱。對于聲明式事務,事務名稱始終是

全限定類名稱

+'.' +

通知類的方法名稱

。例如,如果

BusinessService

handlePayment(..)

方法啟動了事務,則事務的名稱将為:

com.example.BusinessService.handlePayment

具有@Transactional的多個事務管理器

大多數Spring應用程式僅需要一個事務管理器,但是在某些情況下,你可能需要在一個應用程式中使用多個獨立的事務管理器。你可以使用

@Transactional

注解的

value

transactionManager

屬性來選擇指定要使用的

TransactionManager

。這可以是事務管理器bean的bean名稱或限定符值。例如,使用限定符表示法,可以在應用程式上下文中将以下Java代碼與以下事務管理器bean聲明進行組合:

public class TransactionalService {

    @Transactional("order")
    public void setSomething(String name) { ... }

    @Transactional("account")
    public void doSomething() { ... }

    @Transactional("reactive-account")
    public Mono<Void> doSomethingReactive() { ... }
}           

以下清單顯示了bean聲明:

<tx:annotation-driven/>

    <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        ...
        <qualifier value="order"/>
    </bean>

    <bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        ...
        <qualifier value="account"/>
    </bean>

    <bean id="transactionManager3" class="org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager">
        ...
        <qualifier value="reactive-account"/>
    </bean>
</tx:annotation-driven>           

在這種情況下,

TransactionalService

上的各個方法在單獨的事務管理器下運作,并根據

order

account

reactive-account

限定符進行區分。如果未找到特别限定的

TransactionManager

bean,則仍使用預設的

<tx:annotation-driven>

目标bean名稱

transactionManager

org.liyong.dataaccess.starter.QualifierAnnotationTransactionManagerIocContainer

自定義組合的注解

如果你發現在許多不同的方法上将

@Transactional

重複使用相同的屬性,則Spring的元注解支援可讓你為特定用例定義自定義的注解。例如,考慮以下注解定義:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("order")
public @interface OrderTx {
}

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("account")
public @interface AccountTx {
}           

前面的注解使我們可以按照上一節的内容編寫示例,如下所示:

public class TransactionalService {

    @OrderTx
    public void setSomething(String name) {
        // ...
    }

    @AccountTx
    public void doSomething() {
        // ...
    }
}           

在前面的示例中,我們使用了文法來定義事務管理器限定符,但是我們還可以包括傳播行為,復原規則、逾時和其他功能。

org.liyong.dataaccess.starter.CustomAnnotationTransactionManagerIocContainer

1.4.7 事物傳播屬性

本節描述了Spring中事務傳播的一些語義。請注意,本節不是對事務傳播的全面介紹。相反,它詳細介紹了有關Spring中事務傳播的一些語義。

在Spring管理的事務中,請注意實體事務和邏輯事務之間的差異,以及傳播設定如何應用于此差異。

了解

PROPAGATION_REQUIRED

Spring 5 中文解析資料存儲篇-事務同步和聲明式事物管理

PROPAGATION_REQUIRED

強制執行一個實體事務,如果尚不存在目前事務,則在本地為目前範圍建立新事物執行,或參與為更大範圍定義的現有“外部”事務。在同一線程内的公共調用堆棧中,這是一個很好的預設值(例如,一個服務

facade

委托給幾個存儲庫方法,其中所有底層資源都必須參與服務級事務)。(譯者:如果存在一個事務,則支援目前事務。如果沒有事務則開啟一個新的事務)

預設情況下,參與的事務會加入外部作用域的特征,而忽略本地隔離級别、逾時值或隻讀标志(如果有)。如果希望在參與具有不同隔離級别的現有事務時拒絕隔離級别聲明,請考慮在事務管理器上将

validateExistingTransactions

标志切換為

true

。這種非寬容模式還拒絕隻讀不比對項(即,内部的讀寫事務試圖參與隻讀的外部作用域)。

當傳播設定為

PROPAGATION_REQUIRED

時,将為應用該設定的每種方法建立一個邏輯事務作用域。每個此類邏輯事務作用域都可以單獨确定復原狀态,而外部事務作用域在邏輯上與内部事務作用域無關。對于标準

PROPAGATION_REQUIRED

行為,所有這些範圍都映射到同一實體事務。是以,内部事務範圍中設定的復原标記會影響外部事務實際送出的機會。

但是,在内部事務範圍設定復原标記的情況下,外部事務尚未決定復原本身,是以復原(由内部事務範圍默默觸發)是意外的。此時将引發相應的

UnexpectedRollbackException

。這是預期的行為,是以永遠不會誤導事務的調用方以假定在确實未執行送出的情況下執行該送出。是以,如果内部事務(外部調用者不知道)将事務無提示地标記為隻復原,則外部調用者仍會調用

commit

。外部調用者需要接收一個

UnexpectedRollbackException

,以清楚地表示已執行復原。

PROPAGATION_REQUIRES_NEW

Spring 5 中文解析資料存儲篇-事務同步和聲明式事物管理

PROPAGATION_REQUIRED

相比,

PROPAGATION_REQUIRES_NEW

始終對每個受影響的事務範圍使用獨立的實體事務,而從不參與外部範圍的現有事務。在這種安排中,基礎資源事務是不同的,是以可以獨立地送出或復原,而外部事務不受内部事務復原狀态的影響,并且内部事務在完成後立即釋放鎖。這種獨立的内部事務還可以聲明其自己的隔離級别、逾時和隻讀設定,而不會繼承外部事務的特征。(譯者:總是開啟一個新的事務。如果一個事務已經存在,則将這個存在的事務挂起。)

PROPAGATION_NESTED

PROPAGATION_NESTED

使用具有多個儲存點的單個實體事務,它可以復原到該儲存點。這種部分復原使内部事務範圍觸發其範圍的復原,盡管某些操作已復原,但外部事務仍能夠繼續實體事務。此設定通常映射到

JDBC

儲存點,是以僅适用于

JDBC

資源事務。請參閱Spring的

DataSourceTransactionManager

。(譯者:如果一個活動的事務存在,則運作在一個嵌套的事務中. 如果沒有活動事務, 則按

TransactionDefinition.PROPAGATION_REQUIRED

屬性執行)

org.liyong.dataaccess.starter.PropagationTransactionManagerIocContainer

1.4.8 通知事務操作

假設你要同時運作事務性操作和一些基本的配置通知。你如何在

<tx:annotation-driven/>

的上下文中實作此目的?

當你調用

updateFoo(Foo)

方法時,你想要檢視以下操作:

  • 已配置的分析切面啟動。
  • 事務通知運作。
  • 通知對象上的方法運作。
  • 事物送出。
  • 分析切面報告整個事務方法調用的确切持續時間。
本章不涉及任何詳細的AOP解釋(除非它适用于事務)。有關 AOP 配置和AOP的詳細介紹,請參見AOP。

以下代碼顯示了前面讨論的簡單配置方面:

package x.y;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
import org.springframework.core.Ordered;

public class SimpleProfiler implements Ordered {

    private int order;

    // allows us to control the ordering of advice
    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    // this method is the around advice
    public Object profile(ProceedingJoinPoint call) throws Throwable {
        Object returnValue;
        StopWatch clock = new StopWatch(getClass().getName());
        try {
            clock.start(call.toShortString());
            returnValue = call.proceed();
        } finally {
            clock.stop();
            System.out.println(clock.prettyPrint());
        }
        return returnValue;
    }
}           

通知的排序是通過

Ordered

接口控制的。有關通知順序的完整詳細資訊,請參閱

。以下配置建立一個

fooService

bean,該bean具有按所需順序應用的概要分析和事務切面:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- this is the aspect -->
    <bean id="profiler" class="x.y.SimpleProfiler">
        <!-- run before the transactional advice (hence the lower order number) -->
        <property name="order" value="1"/>
    </bean>

    <tx:annotation-driven transaction-manager="txManager" order="200"/>

    <aop:config>
            <!-- this advice runs around the transactional advice -->
            <aop:aspect id="profilingAspect" ref="profiler">
                <aop:pointcut id="serviceMethodWithReturnValue"
                        expression="execution(!void x.y..*Service.*(..))"/>
                <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
            </aop:aspect>
    </aop:config>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
        <property name="username" value="scott"/>
        <property name="password" value="tiger"/>
    </bean>

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>           

你可以以類似的方式配置任意數量的其他切面。

下面的示例建立與前兩個示例相同的設定,但是使用純XML聲明性方法:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- the profiling advice -->
    <bean id="profiler" class="x.y.SimpleProfiler">
        <!-- run before the transactional advice (hence the lower order number) -->
        <property name="order" value="1"/>
    </bean>

    <aop:config>
        <aop:pointcut id="entryPointMethod" expression="execution(* x.y..*Service.*(..))"/>
        <!-- runs after the profiling advice (c.f. the order attribute) -->

        <aop:advisor advice-ref="txAdvice" pointcut-ref="entryPointMethod" order="2"/>
        <!-- order value is higher than the profiling aspect -->

        <aop:aspect id="profilingAspect" ref="profiler">
            <aop:pointcut id="serviceMethodWithReturnValue"
                    expression="execution(!void x.y..*Service.*(..))"/>
            <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
        </aop:aspect>

    </aop:config>

    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!-- other <bean/> definitions such as a DataSource and a TransactionManager here -->

</beans>           

上述配置的結果是

fooService

bean,它按此順序應用了分析和事務切面。如果你希望性能分析通知在進來的事務處理通知之後和事務處理通知之前運作,則可以交換性能分析切面bean的

order

屬性的值,使其高于事務處理通知的

order

值。

你可以以類似方式配置其他切面。

1.4.9 在AspectJ中使用

@Transactional

你還可以通過

AspectJ

切面在Spring容器之外使用Spring架構的

@Transactional

支援。為此,請先使用

@Transactional

注解對類(以及可選的類的方法)進行注釋,然後将應用程式與

spring-aspects.jar

檔案中定義的

org.springframework.transaction.aspectj.AnnotationTransactionAspect

連接配接(編織) 。你還必須使用事務管理器配置切面。你可以使用Spring架構的IoC容器來處理依賴項注入切面。配置事務管理切面的最簡單方法是使用

<tx:annotation-driven/>

元素,并如“使用

@Transactional

”中所述,為

Aspectj

指定

mode

屬性。因為這裡我們專注于在Spring容器之外運作的應用程式,是以我們向你展示如何以程式設計方式進行操作。

在繼續之前,你可能需要分别閱讀《使用 》和《 》。

以下示例顯示如何建立事務管理器并配置

AnnotationTransactionAspect

以使用它:

// construct an appropriate transaction manager
DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());

// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);           
使用此切面時,必須注解實作類(或該類中的方法或兩者),而不是注解該類所實作的接口(如果有)。

AspectJ

遵循Java的規則,即不繼承接口上的注解。

類上的

@Transactional

注解指定用于執行該類中任何公共方法的預設事務語義。

類中方法上的

@Transactional

注解将覆寫類注解(如果存在)給出的預設事務語義。你可以注解任何方法,而不管可見性如何(可見性:

public

private

等)。

要使用

AnnotationTransactionAspect

編織應用程式,必須使用

AspectJ

建構應用程式(請參閱

AspectJ開發指南

)或使用加載時編織。

有關使用AspectJ進行加載時編織的讨論

,請參見Spring架構中的使用

AspectJ

進行加載時編織。

作者

個人從事金融行業,就職過易極付、思建科技、某網約車平台等重慶一流技術團隊,目前就職于某銀行負責統一支付系統建設。自身對金融行業有強烈的愛好。同時也實踐大資料、資料存儲、自動化內建和部署、分布式微服務、響應式程式設計、人工智能等領域。同時也熱衷于技術分享創立公衆号和部落格站點對知識體系進行分享。關注公衆号:青年IT男 擷取最新技術文章推送!

部落格位址:

http://youngitman.tech

CSDN:

https://blog.csdn.net/liyong1028826685

微信公衆号:

Spring 5 中文解析資料存儲篇-事務同步和聲明式事物管理

技術交流群:

Spring 5 中文解析資料存儲篇-事務同步和聲明式事物管理