(四)Spring的事務(wù)管理

什么是Spring的事務(wù)管理

??在實際開發(fā)中,操作數(shù)據(jù)庫時都會涉及到事務(wù)管理問題碌补,為此Spring提供了專門用于事務(wù)處理的API棉饶。Spring的事務(wù)管理簡化了傳統(tǒng)的事務(wù)管理流程,并在一定程度上減少了開發(fā)者的工作量照藻。
??為Spring提供事務(wù)管理的jar包spring-tx岩梳。

注意:要使用Spring的事務(wù)管理,請先去了解數(shù)據(jù)庫事務(wù)管理基礎(chǔ)知識(并發(fā)冀值、鎖、隔離級別等等)滑蚯。

ThreadLocal

??從前面的文章我們知道抵栈,Spring通過各類模板類降低了開發(fā)者使用各種數(shù)據(jù)持久化技術(shù)的難度古劲。這些模板類都是線程安全的,也就是說疤剑,多個DAO可以復(fù)用同一個模板實例而不會發(fā)生沖突隘膘。使用模板類訪問底層數(shù)據(jù),根據(jù)持久化技術(shù)的不同纵势,模板類需要綁定數(shù)據(jù)連接或會話的資源钦铁。但這些資源本身是非線程安全的蹋嵌,也就是說它們不能在同一時刻被多個線程共享。雖然模板類通過資源獲取數(shù)據(jù)連接或會話躏仇,但資源池本身解決的是數(shù)據(jù)連接或會話的緩存問題,并非數(shù)據(jù)連接或會話的線程安全問題腺办。
??按照傳統(tǒng)經(jīng)驗焰手,如果某個對象是非線程安全的,在多線程環(huán)境下怀喉,對對象的訪問必須采用synchronized進行線程同步。但模板類并未采用線程同步機制躬拢,因為線程同步會降低并發(fā)性,影響系統(tǒng)性能聊闯。此外工猜,通過代碼同步解決線程安全的挑戰(zhàn)性很大,可能會增加幾倍的實現(xiàn)難度菱蔬。那么,模板類是用了什么魔法使得可以在無須線程同步的情況下就解決線程安全的難題呢拴泌?答案就是ThreadLocal魏身。
??ThreadLocal在Spring中發(fā)揮著重要的作用,在管理request作用域的Bean蚪腐、事務(wù)管理、任務(wù)調(diào)度回季、AOP等模塊都出現(xiàn)了它的身影宙枷。要想了解Spring事務(wù)管理的底層技術(shù)慰丛,必須明白ThreadLocal是什么。

ThreadLocal是什么贤笆?

??ThreadLocal,顧名思義芥永,它不是一個線程埋涧,而是保存線程本地化對象的容器保存線程本地化對象的容器奇瘦,保存線程本地化對象的容器<摺!耳标!當(dāng)運行于多線程環(huán)境的某個對象使用ThreadLocal維護變量時醇坝,ThreadLocal為每個使用該變量的線程分配一個獨立的變量副本。所以每個線程都可以獨立地改變自己的副本次坡,而不會影響其他線程所對應(yīng)的變量副本呼猪。從線程的角度看,這個變量就像線程專用的本地變量砸琅。
??可以總結(jié)為一句話:ThreadLocal的作用是提供線程內(nèi)的局部變量宋距,這種變量在線程的生命周期內(nèi)起作用,減少同一個線程內(nèi)多個函數(shù)或者組件之間一些公共變量的傳遞的復(fù)雜度明棍。
??InheritableThreadLocal繼承與ThreadLocal乡革,它會自動為子線程復(fù)制一份從父線程那里繼承而來的本地變量:在創(chuàng)建子線程時,子線程會接受所有可繼承的線程本地變量的初始值摊腋。當(dāng)必須將本地線程變量自動傳送給所有創(chuàng)建的子線程時沸版,應(yīng)盡可能地使用;InheritableThreadLocal,而非ThreadLocal兴蒸。

推薦一篇文章知乎:ThreadLocal和synchronized的區(qū)別?

ThreadLocal與synchronized比較

??它們都是為了解決多線程中相同變量的訪問沖突問題视粮。那么,ThreadLocal有什么優(yōu)勢橙凳?
??在synchronized同步機制中蕾殴,通過對象的鎖機制保證同一時間只有一個線程訪問變量笑撞。這時該變量是多個線程共享的,使用同步機制要求程序縝密地分析什么時候?qū)ψ兞窟M行讀/寫钓觉,什么時候需要鎖定某個對象茴肥、什么時候釋放對象鎖等繁雜的問題,程序設(shè)計和編寫難度相對較大荡灾。
??而ThreadLocal從另一個角度來解決多線程的并發(fā)訪問瓤狐。ThreadLocal為每個線程提供了一個獨立的變量副本,從而隔離了多個線程對訪問數(shù)據(jù)的沖突問題批幌。因為每個線程都擁有自己的變量副本础锐,因而也就沒有必要對該變量進行同步。ThreadLocal提供了線程安全的對象封裝荧缘,在編寫多線程代碼時皆警,可以把不安全的變量封裝進ThreadLocal

Spring中的ThreadLocal

??我們知道在一般情況下截粗,只有無狀態(tài)的Bean才可以在多線程環(huán)境下共享信姓,在Spring中, 絕大部分Bean都可以聲明為singleton作用域桐愉。就是因為Spring對一些Bean(如RequestContextHolder财破、 TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態(tài)采用 ThreadLocal進行處理从诲,讓它們也成為線程安全的狀態(tài)左痢,因為有狀態(tài)的Bean就可以在多線程中共享了。

ThreadLocal總結(jié)

??對于多線程資源共享的問題系洛,同步機制采用了“以時間換空間”的方式:訪問串行化俊性、對象共享化;而ThreadLocal采用了“以空間換時間”的方式:訪問并行化描扯,對象獨享化定页。前者僅提供一份變量,讓不同的線程排隊訪問绽诚;而后者為每個線程都提供了一份變量典徊,因此可以同時訪問而互不影響。

Spring的事務(wù)管理

??Spring為事務(wù)管理提供了一致的編程模板恩够,在高層次建立了統(tǒng)一的事務(wù)抽象卒落。也就是說,不管是選擇Spring JDBC蜂桶、Hibernate儡毕、JPA還是選擇Mybatis,Spring都可以讓用戶用統(tǒng)一的編程模型進行事務(wù)管理扑媚。
??像Spring DAO為不同的持久化實現(xiàn)提供了模板類一樣腰湾,Spring事務(wù)管理繼承了這一風(fēng)格雷恃,也提供了事務(wù)模板了TransactionTemplate,通過它并配合使用事務(wù)回調(diào)TransactionCallback指定具體的持久化操作费坊,就可以通過編程方式實現(xiàn)事務(wù)管理倒槐,而無需關(guān)注資源獲取、復(fù)用葵萎、釋放导犹、事務(wù)同步和異常處理等操作。
??Spring事務(wù)管理的亮點在于聲明式事務(wù)管理羡忘。Spring允許通過聲明方式,在IOC配置中指定事務(wù)的邊界和事務(wù)屬性磕昼,Spring自動在指定的事務(wù)邊界上應(yīng)用事務(wù)屬性。

事務(wù)管理的核心接口

??在Spring事務(wù)管理SPI(Service Provider Interface)的抽象層主要包括3個接口,分別是PlatformTransactionManager睛竣、TransactionDefinitionTransactionStatus罩抗,它們位于org.springframework.transaction包中,三者間的關(guān)系如下:


??TransactionDefinition用于描述事務(wù)的隔離級別峰鄙、超時時間浸间、是否為只讀事務(wù)和事務(wù)傳播規(guī)則等控制事務(wù)具體行為的事務(wù)屬性,這些事務(wù)實現(xiàn)可以通過XML配置或注解描述提供吟榴,也可以通過手工編程的方式設(shè)置魁蒜。
??PlatformTransactionManager根據(jù)TransactionDefinition提供的事務(wù)屬性配置信息創(chuàng)建事務(wù),并用TransactionStatus描述這個激活事務(wù)的狀態(tài)吩翻。下面分別描述這些接口:

TransactionDefinition

??該接口定義了Spring兼容的事務(wù)屬性兜看,這些屬性對事務(wù)管理控制的若干方面進行配置。

  • 事務(wù)隔離:當(dāng)前事務(wù)和其他事務(wù)的隔離程度狭瞎。
  • 事務(wù)傳播:通常在一個事務(wù)中執(zhí)行的所有代碼都會允許在同一事務(wù)上下文中细移。
  • 事務(wù)超時:事務(wù)在超時前能允許多久,超過時間后熊锭,事務(wù)被回滾弧轧。
  • 只讀狀態(tài):只讀事務(wù)不修改任何數(shù)據(jù),資源事務(wù)管理者可以針對可讀事務(wù)應(yīng)用一些優(yōu)化措施碗殷,提高運行性能精绎。
    ??Spirng允許通過XML或注解元數(shù)據(jù)的方式為一個有事務(wù)要求的服務(wù)類方法配置事務(wù)屬性,這些信息作為Spring事務(wù)管理框架的“輸入”亿扁,Spring將自動按事務(wù)屬性信息的指示捺典,為目標(biāo)方法提供相應(yīng)的事務(wù)支持。
TransactionStatus

??該接口代表一個事務(wù)的具體運行狀態(tài)从祝。事務(wù)管理者可以通過該接口獲取事務(wù)運行期的狀態(tài)信息襟己,也可以通過該接口間接地回滾事務(wù)引谜,它相比于拋出異常時回滾事務(wù)的方式更具可控性。

PlatformTransactionManager

??通過JDBC的事務(wù)管理知識可以知道擎浴,事務(wù)只能被提交或回滾(或回滾到某個保存點后提交)员咽,而該接口很好地描述了事務(wù)管理這個概念,解釋見代碼注釋:

public interface PlatformTransactionManager {
     //該方法根據(jù)事務(wù)定義信息從事務(wù)環(huán)境中返回一個已存在的事務(wù)贮预,或者創(chuàng)建一個新的事務(wù)贝室,并用TransacitonStatus描述這個事務(wù)的狀態(tài)
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    //根據(jù)事務(wù)的狀態(tài)提交事務(wù)。如果事務(wù)已經(jīng)被標(biāo)識為rollback-only仿吞,則該方法將執(zhí)行一個回滾事務(wù)的操作
    void commit(TransactionStatus var1) throws TransactionException;
    //將事務(wù)回滾滑频。當(dāng)commit()方法拋出異常時,該方法會被隱式調(diào)用
    void rollback(TransactionStatus var1) throws TransactionException;
}

事務(wù)管理器實現(xiàn)類

??Spring將事務(wù)管理委托給底層具體的持久化實現(xiàn)框架來完成。因此為不同的框架提供了PlatformTransactionManager接口的實現(xiàn)類唤冈,如下圖

選中框架的常用

??這些事務(wù)管理器都是對特定事務(wù)實現(xiàn)框架的代理峡迷,這樣就可以通過Spring所提交的高級抽象對不同種類的事務(wù)實現(xiàn)使用相同的方式進行管理,而不同關(guān)心具體的實現(xiàn)你虹。
??要實現(xiàn)事務(wù)管理绘搞,首先要在Spring中配置好相應(yīng)的事務(wù)管理器,為事務(wù)管理器指定數(shù)據(jù)資源及一些其他事務(wù)管理控制屬性傅物,下面列出常見框架的配置夯辖。

1.Spring JDBC 和Mybatis框架配置

??如果使用Spring JDBC或Mybatis,由于它們都基于數(shù)據(jù)源的Connection訪問數(shù)據(jù)庫董饰,所以可以使用DataSourceTransactionManager蒿褂,只要在Spring中進行如下配置即可:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd">
    <!--1.加載指定文件以配置數(shù)據(jù)庫相關(guān)參數(shù)屬性:${xxx}-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--2.定義數(shù)據(jù)源-->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"
          p:driverClassName="${jdbc.driverClass}"
          p:url="${jdbc.url}"
          p:username="${jdbc.username}"
          p:password="${jdbc.password}"
    />
    <!--3.基于相應(yīng)數(shù)據(jù)源的事務(wù)管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource" />
</beans>
2.Hibernate框架配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd">

    <!--1.加載指定文件以配置數(shù)據(jù)庫相關(guān)參數(shù)屬性:${xxx}-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--2.定義數(shù)據(jù)源,配置連接池屬性和c3p0私有屬性-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
          p:driverClass="${jdbc.driverClass}"
          p:jdbcUrl="${jdbc.url}"
          p:user="${jdbc.username}"
          p:password="${jdbc.password}"

          p:maxPoolSize="30"
          p:minPoolSize="10"
          p:autoCommitOnClose="false"
          p:checkoutTimeout="10000"
          p:acquireRetryAttempts="2"
    />
    <!--3.配置SqlSessionFactoryBean對象:注入數(shù)據(jù)源,配置mybatis全局文件尖阔,掃描entity包贮缅,使用別名,掃描sql配置文件-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
        p:dataSource-ref="dataSource"
        p:configLocation="classpath:mybatis-config.xml"
        p:typeAliasesPackage="cn.wk.entity"
        p:mapperLocations="classpath:mapper/*.xml"
    />

    <!--4.配置掃描Dao接口包,動態(tài)實現(xiàn)Dao接口介却,注入到Spring容器中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"
        p:sqlSessionFactoryBeanName="sqlSessionFactory"
        p:basePackage="cn.wk.dao"
    />
</beans>

事務(wù)同步管理器(暫時了解)

??Spring將JDBC的Connection谴供、Hibernate的Session等訪問數(shù)據(jù)庫的連接或會話統(tǒng)稱為資源,這些資源在同一時刻是不能多線程共享的齿坷。為了讓DAO桂肌、Service類可以做到singleton,Spring的事務(wù)同步管理器類org.springframework.transaction.support.TransactionSynchronizationManager使用ThreadLocal為不同事務(wù)線程提供了獨立的資源副本永淌,同時維護事務(wù)配置的屬性和運行狀態(tài)信息崎场。
??Spring框架為不同的持久化技術(shù)提供了一套從TransactionSynchronizationManager獲取對應(yīng)線程綁定資源的工具類,如下圖:


??這些工具類都提供了靜態(tài)的方法遂蛀,通過這些方法可以獲取和當(dāng)前線程綁定的資源谭跨。

事務(wù)傳播行為

??當(dāng)我們調(diào)用一個基于Spring 的Service接口方法(如UserServiceadduser()方法)時,它將運行于Spring管理的事務(wù)環(huán)境中,Service接口方法可能會在內(nèi)部調(diào)用其他的Service接口方法以共同完成一個完整的業(yè)務(wù)操作螃宙,因此就會產(chǎn)生服務(wù)接口方法嵌套調(diào)用的情況蛮瞄,Spring通過事務(wù)傳播行為控制當(dāng)前的事務(wù)如何傳播到被簽到調(diào)用的目標(biāo)服務(wù)接口方法中。
??Spring在TransactionDefinition接口中規(guī)定了7中類型的事務(wù)傳播行為谆扎,它們規(guī)定了事務(wù)方法和事務(wù)方法發(fā)生嵌套調(diào)用時事務(wù)如何進行傳播挂捅,如下圖:

Spring事務(wù)管理的兩種方式

  • 編程式事務(wù)管理:通過編寫代碼實現(xiàn)的事務(wù)管理,包括定義事務(wù)的開始堂湖、正常執(zhí)行后的事務(wù)提交和異常時的事務(wù)回滾闲先。
  • 聲明式事務(wù)管理:通過AOP技術(shù)實現(xiàn)的事務(wù)管理,主要思想是將事務(wù)作為一個“切面”代碼單獨編寫无蜂,然后通過AOP技術(shù)將事務(wù)管理的“切面”植入到業(yè)務(wù)目標(biāo)類中伺糠。

??聲明式事務(wù)管理的最大優(yōu)點在于開發(fā)者無需通過編程的方式來管理事務(wù),只需在配置文件中進行相關(guān)的事務(wù)規(guī)則聲明酱讶,就可以將事務(wù)應(yīng)用到業(yè)務(wù)邏輯中退盯。這使得開發(fā)人員可以更加專注于核心業(yè)務(wù)邏輯代碼的編寫,在一定程度上減少了工作量泻肯,提高了開發(fā)效率,所以在實際開發(fā)中慰照,通常都推薦使用聲明式事務(wù)管理灶挟。

編程式的事務(wù)管理(略)

聲明式事務(wù)管理

??大多數(shù)Spring用戶選擇聲明式事務(wù)管理的功能,這種方式對代碼的侵入性最小毒租,可以讓事務(wù)管理代碼完全從業(yè)務(wù)代碼中移除稚铣,非常復(fù)合非侵入式輕量級容器的理念。
??Spring的聲明式事務(wù)管理是通過SpringAOP實現(xiàn)的墅垮,通過事務(wù)的聲明式信息惕医,Spring負責(zé)將事務(wù)管理增強邏輯東塔織入業(yè)務(wù)方法的相應(yīng)連接點中。這些邏輯包括獲取線程綁定資源算色、開始事務(wù)抬伺、提交/回滾事務(wù)、進行異常轉(zhuǎn)換和處理等工作灾梦。
??在Spring早期版本中峡钓,用戶必須通過TransactionProxyFactoryBean代理類對需要事務(wù)管理的業(yè)務(wù)類進行代理,以便實施事務(wù)功能的增強∪艉樱現(xiàn)在我們可以通過aop/tx命名空間聲明事務(wù)能岩,因此代理類實施聲明式事務(wù)的方法基本不再使用。當(dāng)然萧福,了解TransactionProxyFactoryBean有助于我們更直觀地理解Spring實施聲明式事務(wù)的內(nèi)部工作原理拉鹃,通過一個例子來了解:
1.創(chuàng)建一個業(yè)務(wù)Bean

@Service
@Transactional
public class BbtForum{
    public ForumDao forumDao;
    public TopicDao topicDao;
    public PostDao postDao;
    public void addTopic(Top topic){
          topicDao.addTopic(topic);
          postDao.addPost(topic.getPost());
    }

    public  forum getForun(int forumId){
          return forumDao.getForum(forumId);
    }
    
    public void updateForum(Forum forum){
          forumDao.updateForum(forum);
    }

    public int getForumNum(){
          return forumDao.getForumNum();
    }
}

??該類有4個方法,我們希望addTopicupdateForum()方法擁有寫事務(wù)的能力,而其他兩個方法只需要有讀事務(wù)的能力就可以了膏燕。
2.使用TransactionProxyFactoryBean配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd">
    <!--1.加載指定文件以配置數(shù)據(jù)庫相關(guān)參數(shù)屬性:${xxx}-->
    <context:property-placeholder location="classpath:db.properties"/>
    <!--2.定義數(shù)據(jù)源-->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"
          p:driverClassName="${jdbc.driverClass}"
          p:url="${jdbc.url}"
          p:username="${jdbc.username}"
          p:password="${jdbc.password}"
    />
    <!--3.基于相應(yīng)數(shù)據(jù)源的事務(wù)管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource" />

    <!--需要實施事務(wù)增強的業(yè)務(wù)Bean-->
    <bean id="bbtForumTarget" class="cn.wk.chapter11.service.BbtForum"
          p:forumDao-ref="forumDao"
          p:topicDao-ref="topicDao"
          p:postDao-ref="postDao"
    />

    <!--使用事務(wù)代理工廠為目標(biāo)業(yè)務(wù)Bean提供事務(wù)增強-->
    <bean id="bbtForum" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
          p:transactionManager-ref="transactionManager"
          p:target-ref="bbtForumTarget">
        <property name="transactionAttributes">
            <props>
                <!--只讀事務(wù)-->
                <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
                <!--可寫事務(wù)-->
                <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
</beans>

??按照約定的習(xí)慣钥屈,需要事務(wù)增強的業(yè)務(wù)類一般將id取名為xxTarget,這可以在字面上表示該Bean是要被代理的目標(biāo)Bean煌寇。
??通過TransactionProxyFactoryBean對業(yè)務(wù)類進行代理焕蹄,織入事務(wù)增強功能。首先阀溶,需要為該代理類指定事務(wù)管理器腻脏,這些事務(wù)管理器實現(xiàn)了PlatformTransactionManager接口;其次银锻,通過target屬性指定需要代理的目標(biāo)Bean永品;最后,為業(yè)務(wù)Bean的不同方法配置事務(wù)屬性击纬。Spring允許通過鍵值配置業(yè)務(wù)方法的事務(wù)屬性信息鼎姐,鍵可以使用通配符,如get*代表目標(biāo)類中所有以get為前綴的方法更振,它匹配BbtForumgetForum(int forumId)getForumNum()方法炕桨;而key="*"代表匹配BbtForum接口的所有方法。
??<prop>內(nèi)的值為事務(wù)屬性信息肯腕,配置格式如下:

事務(wù)屬性設(shè)置格式

基于XML配置的聲明式事務(wù)

??使用TransactionProxyFactoryBean代理工廠類為業(yè)務(wù)類添加事務(wù)支持缺點是配置過于繁瑣献宫,所以Spring后來在配置中添加了一個tx命名空間,在配置文件中以明確結(jié)構(gòu)化的方式定義事務(wù)屬性实撒,大大提高了配置事務(wù)屬性的便利性姊途,<tx:advice>標(biāo)簽用法格式如下:

??我們用tx和aop命名空間對前面基于FactoryBean的事務(wù)配置方式進行替換,代碼如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-context.xsd">
    <!--1.加載指定文件以配置數(shù)據(jù)庫相關(guān)參數(shù)屬性:${xxx}-->
    <context:property-placeholder location="classpath:db.properties"/>
    <!--2.定義數(shù)據(jù)源-->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"
          p:driverClassName="${jdbc.driverClass}"
          p:url="${jdbc.url}"
          p:username="${jdbc.username}"
          p:password="${jdbc.password}"
    />
    <!--3.基于相應(yīng)數(shù)據(jù)源的事務(wù)管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"/>

    <!--4.使用強大的切點表達式語言輕松定義目標(biāo)方法-->
    <aop:config>
        <!--通過aop定義事務(wù)增強切面-->
        <aop:pointcut id="serviceMethod" expression="execution(* cn.wk.chapter11.service.*Forum.*(..))"/>
        <!--引用事務(wù)增強-->
        <aop:advisor advice-ref="serviceMethod" advice-ref="txAdvice"/>
    </aop:config>

    <!--5.事務(wù)增強-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--屬性定義-->
        <tx:attributes>
            <tx:method name="get" read-only="false"/>
            <tx:method name="add*" rollback-for="Exception"/>
            <tx:method name="update"/>
        </tx:attributes>
    </tx:advice>
</beans>

??在這一過程中我們看到了3種角色:通過aop/tx定義的聲明式事務(wù)配置信息知态、業(yè)務(wù)Bean捷兰、Spring容器。Spring容器自動將第一者應(yīng)用于第二者负敏,從容器中返回的業(yè)務(wù)Bean已經(jīng)是被織入事務(wù)增強的代理Bean贡茅,即第一者和第二者在配置時不直接發(fā)生關(guān)系。
??而在使用TransactionProxyFactoryBean進行事務(wù)配置時原在,它需要直接通過target屬性引用目標(biāo)業(yè)務(wù)Bean友扰,結(jié)果造成目標(biāo)業(yè)務(wù)Bean往往需要使用target進行命名,以避免和最終代理Bean名沖突。使用aop/tx方式后庶柿,業(yè)務(wù)Bean的名稱不需要做任何“配合性”的調(diào)整村怪,aop直接通過切點表達式語言就可以對業(yè)務(wù)Bean進行定位。從這個意義聲來說浮庐,aop/tx的配置方式對業(yè)務(wù)Bean是“無侵入”的甚负,而代理類的配置顯然是“侵入式”的柬焕。
??在aop的命名空間中,通過切點表達式語言梭域,將cn.wk.chapter11.service包下所有以Forum為后綴的類納入了需要進行事務(wù)增強的范圍斑举,并配合<tx:advice><aop:advisor>完成了事務(wù)切面的定義。<aop:advisor>引用的txAdvice增強是在tx命名空間上定義的病涨。首先富玷,事務(wù)增強一定需要一個事務(wù)管理器的支持,<tx;advice>通過transaction屬性引用了定義好的事務(wù)管理器既穆。曾經(jīng)摻雜在一起赎懦,以逗號分割字符串定義的事務(wù)屬性,現(xiàn)在變成了一個清晰的XML片段幻工,十分簡潔励两。<tx:method>元素用于的屬性如下:


??如果需要為不同的業(yè)務(wù)類Bean應(yīng)用不同的事務(wù)管理風(fēng)格,則可以在<aop;config>中定義另外多套事務(wù)切面囊颅,具體的配置方法在現(xiàn)有基礎(chǔ)上演繹即可当悔。

基于注解的聲明式事務(wù)(常用)

??除了基于XML的事務(wù)配置,Spring還提供了基于注解的事務(wù)配置踢代,即通過@Transactional對需要事務(wù)增強的Bean接口盲憎、實現(xiàn)類或方法進行標(biāo)注;在容器中配置基于注解的事務(wù)增強驅(qū)動胳挎,即可啟用基于注解的聲明式事務(wù)”号希現(xiàn)在項目中基本都采用這種配置。下面為配置步驟:
1.在需要事務(wù)管理的業(yè)務(wù)Bean前加上一個@Transactional注解:

@Service
@Transactional
public class BbtForum{
    .......
}

2.在配置文件加入標(biāo)簽

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-context.xsd">
    <!--1.掃描service包注冊以注解方式聲明的Bean-->
    <context:component-scan base-package="cn.wk.crm.service"/>

    <!--2.配置事務(wù)管理器,注入數(shù)據(jù)庫連接池-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"
    />
    <!--3.注解驅(qū)動會對標(biāo)注@Transactional的Bean進行加工處理串远,以織入事務(wù)管理切面-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

注:數(shù)據(jù)源一般在dao層的xml文件配置,此處直接引用了儿惫,詳見Spring第三篇文章澡罚。
??在默認情況下,<tx:annotation-driven>會默認使用名為transactionManager的事務(wù)管理器。所以肾请,如果用戶的事務(wù)管理器id為transactionManager留搔,則可以進一步簡化配置為:

<tx:annotation-driven/>
@Transactional注解屬性

??和XML配置方式一樣,該注解也擁有一組普適性很強的默認事務(wù)屬性铛铁,往往可以直接使用這些默認屬性隔显,具體如下:

  • 事務(wù)傳播行為:PROPAGATION_REQUIRED
  • 事務(wù)隔離級別:ISOLATION_DEFAULT
  • 讀寫事務(wù)屬性:讀/寫事務(wù)。
  • 超時時間:依賴于底層的事務(wù)系統(tǒng)的默認值饵逐。
  • 回滾設(shè)置:任何運行期異常引發(fā)回滾括眠,任何檢查型異常不會引發(fā)回滾。

??這些默認設(shè)置在大多數(shù)情況下都適用倍权,當(dāng)然掷豺,Spring也運行通過手工設(shè)定屬性值覆蓋默認值。


@Transactional注解的兩個位置

??@Transactional注解可以在類上使用,表示事務(wù)的設(shè)置對整個類上的方法都起作用当船,也可以在方法處使用题画,表示事務(wù)的設(shè)置只對該方法有效,如果即在類上加上該注解德频,又在類中方法上加上該注解苍息,則方法上的注解會覆蓋類上的注解。

參考資料

《精通Spring 4.x 企業(yè)應(yīng)用開發(fā)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末壹置,一起剝皮案震驚了整個濱河市竞思,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蒸绩,老刑警劉巖衙四,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異患亿,居然都是意外死亡传蹈,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門步藕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惦界,“玉大人,你說我怎么就攤上這事咙冗≌赐幔” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵雾消,是天一觀的道長灾搏。 經(jīng)常有香客問我,道長立润,這世上最難降的妖魔是什么狂窑? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮桑腮,結(jié)果婚禮上泉哈,老公的妹妹穿的比我還像新娘。我一直安慰自己破讨,他們只是感情好丛晦,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著提陶,像睡著了一般烫沙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上搁骑,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天斧吐,我揣著相機與錄音又固,去河邊找鬼。 笑死煤率,一個胖子當(dāng)著我的面吹牛仰冠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蝶糯,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼洋只,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了昼捍?” 一聲冷哼從身側(cè)響起识虚,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妒茬,沒想到半個月后担锤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡乍钻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年肛循,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片银择。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡多糠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出浩考,到底是詐尸還是另有隱情夹孔,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布析孽,位于F島的核電站搭伤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏袜瞬。R本人自食惡果不足惜闷畸,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吞滞。 院中可真熱鬧,春花似錦盾沫、人聲如沸裁赠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佩捞。三九已至,卻和暖如春蕾哟,著一層夾襖步出監(jiān)牢的瞬間一忱,已是汗流浹背莲蜘。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留帘营,地道東北人票渠。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像芬迄,于是被迫代替她去往敵國和親问顷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容