1. 程式人生 > >Spring 事務機制簡述

Spring 事務機制簡述

概述

事務管理對於企業應用來說是至關重要的,即使出現異常情況,它也可以保證資料的一致性。
Spring Framework對事務管理提供了一致的抽象,其特點如下:

  • 為不同的事務API提供一致的程式設計模型,比如JTA(Java Transaction API), JDBC, Hibernate, JPA(Java Persistence API和JDO(Java Data Objects)
  • 支援宣告式事務管理,特別是基於註解的宣告式事務管理,簡單易用
  • 提供比其他事務API如JTA更簡單的程式設計式事務管理API
  • 與spring資料訪問抽象的完美整合

程式設計式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於程式設計式事務管理,spring推薦使用TransactionTemplate。

Spring宣告式事務讓我們從複雜的事務處理中得到解脫。使得我們再也無需要去處理獲得連線、關閉連線、事務提交和回滾等這些操作。再也無需要我們在與事務相關的方法中處理大量的try…catch…finally程式碼。我們在使用Spring宣告式事務時,有一個非常重要的概念就是事務屬性。事務屬性通常由事務的傳播行為,事務的隔離級別,事務的超時值和事務只讀標誌組成。我們在進行事務劃分時,需要進行事務定義,也就是配置事務的屬性。

宣告式事務管理建立在AOP之上的。其本質是對方法前後進行攔截,然後在目標方法開始之前建立或者加入一個事務,在執行完目標方法之後根據執行情況提交或者回滾事務。宣告式事務最大的優點就是不需要通過程式設計的方式管理事務,這樣就不需要在業務邏輯程式碼中摻雜事務管理的程式碼,只需在配置檔案中做相關的事務規則宣告(或通過基於@Transactional註解的方式),便可以將事務規則應用到業務邏輯中。

顯然宣告式事務管理要優於程式設計式事務管理,這正是spring倡導的非侵入式的開發方式。宣告式事務管理使業務程式碼不受汙染,一個普通的POJO物件,只要加上註解就可以獲得完全的事務支援。和程式設計式事務相比,宣告式事務唯一不足地方是,後者的最細粒度只能作用到方法級別,無法做到像程式設計式事務那樣可以作用到程式碼塊級別。但是即便有這樣的需求,也存在很多變通的方法,比如,可以將需要進行事務管理的程式碼塊獨立為方法等等。

宣告式事務管理也有兩種常用的方式,一種是基於tx和aop名字空間的xml配置檔案,另一種就是基於@Transactional註解如果同時配置這兩種事務管理方式,優先使用基於@Transactional註解的方式。

顯然基於註解的方式更簡單易用,更清爽。

下面分別詳細講解,事務的四種屬性,僅供諸位學習參考:

Spring在TransactionDefinition介面中定義這些屬性,以供PlatfromTransactionManager使用, PlatfromTransactionManager是spring事務管理的核心介面。

           public interface TransactionDefinition {

int getPropagationBehavior();//返回事務的傳播行為。
int getIsolationLevel();//返回事務的隔離級別,事務管理器根據它來控制另外一個事務可以看到本事務內的哪些資料。
int getTimeout();//返回事務必須在多少秒內完成。
boolean isReadOnly();//事務是否只讀,事務管理器能夠根據這個返回值進行優化,確保事務是隻讀的。
}

1. TransactionDefinition介面中定義五個隔離級別:

  • TransactionDefinition.ISOLATION_DEFAULT:這是預設值,表示使用底層資料庫的預設隔離級別。對大部分資料庫而言,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。另外四個與JDBC的隔離級別相對應;
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的資料。該級別不能防止髒讀,不可重複讀和幻讀,因此很少使用該隔離級別。比如PostgreSQL實際上並沒有此級別。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級別表示一個事務只能讀取另一個事務已經提交的資料。該級別可以防止髒讀,這也是大多數情況下的推薦值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級別表示一個事務在整個過程中可以多次重複執行某個查詢,並且每次返回的記錄都相同。該級別可以防止髒讀和不可重複讀。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止髒讀、不可重複讀以及幻讀。但是這將嚴重影響程式的效能。通常情況下也不會用到該級別。

2. 在TransactionDefinition介面中定義了七個事務傳播行為:

(1)PROPAGATION_REQUIRED 如果存在一個事務,則支援當前事務。如果沒有事務則開啟一個新的事務。

(2)PROPAGATION_SUPPORTS 如果存在一個事務,支援當前事務。如果沒有事務,則非事務的執行。但是對於事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少許不同。

(3)PROPAGATION_MANDATORY 如果已經存在一個事務,支援當前事務。如果沒有一個活動的事務,則丟擲異常。

(4)PROPAGATION_REQUIRES_NEW 總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。

(5)PROPAGATION_NOT_SUPPORTED  總是非事務地執行,並掛起任何存在的事務。使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作為事務管理器。

(6)PROPAGATION_NEVER 總是非事務地執行,如果存在一個活動事務,則丟擲異常;

(7)PROPAGATION_NESTED如果一個活動的事務存在,則執行在一個巢狀的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。這是一個巢狀事務,需要JDBC 驅動的java.sql.Savepoint類。使用PROPAGATION_NESTED,還需要把PlatformTransactionManager的nestedTransactionAllowed屬性設為true;而 nestedTransactionAllowed屬性值預設為false;

PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的區別:它們非常類似,都像一個巢狀事務,如果不存在一個活動的事務,都會開啟一個新的事務。使用 PROPAGATION_REQUIRES_NEW時,內層事務與外層事務就像兩個獨立的事務一樣,一旦內層事務進行了提交後,外層事務不能對其進行回滾。兩個事務互不影響。兩個事務不是一個真正的巢狀事務。

PROPAGATION_REQUIRES_NEW 啟動一個新的, 不依賴於環境的 "內部" 事務. 這個事務將被完全 commited 或 rolled back 而不依賴於外部事務, 它擁有自己的隔離範圍, 自己的鎖, 等等. 當內部事務開始執行時, 外部事務將被掛起, 內務事務結束時, 外部事務將繼續執行。

另一方面, PROPAGATION_NESTED 開始一個 "巢狀的" 事務,  它是已經存在事務的一個真正的子事務. 套事務開始執行時,  它將取得一個 savepoint. 如果這個巢狀事務失敗, 我們將回滾到此 savepoint. 巢狀事務是外部事務的一部分, 只有外部事務結束後它才會被提交。

3. 事務超時

所謂事務超時,就是指一個事務所允許執行的最長時間,如果超過該時間限制但事務還沒有完成,則自動回滾事務。在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。預設設定為底層事務系統的超時值,如果底層資料庫事務系統沒有設定超時值,那麼就是none,沒有超時限制。

4. 事務只讀屬性

只讀事務用於客戶程式碼只讀但不修改資料的情形,只讀事務用於特定情景下的優化,比如使用Hibernate的時候。預設為讀寫事務。

5. 事務回滾規則

指示spring事務管理器回滾一個事務的推薦方法是在當前事務的上下文內丟擲異常。spring事務管理器會捕捉任何未處理的異常,然後依據規則決定是否回滾丟擲異常的事務。

預設配置下,spring只有在丟擲的異常為執行時unchecked異常時才回滾該事務,也就是丟擲的異常為RuntimeException的子類(Errors也會導致事務回滾),而丟擲checked異常則不會導致事務回滾。可以明確的配置在丟擲那些異常時回滾事務,包括checked異常。也可以明確定義那些異常丟擲時不回滾事務。

還可以程式設計性的通過setRollbackOnly()方法來指示一個事務必須回滾,在呼叫完setRollbackOnly()後你所能執行的唯一操作就是回滾。

@Transactional註解

屬性 型別 描述
value String 可選的限定描述符,指定使用的事務管理器
propagation enum: Propagation 可選的事務傳播行為設定
isolation enum: Isolation 可選的事務隔離級別設定
readOnly boolean 讀寫或只讀事務,預設讀寫
timeout int (in seconds granularity) 事務超時時間設定
rollbackFor Class物件陣列,必須繼承自Throwable 導致事務回滾的異常類陣列
rollbackForClassName 類名陣列,必須繼承自Throwable 導致事務回滾的異常類名字陣列
noRollbackFor Class物件陣列,必須繼承自Throwable 不會導致事務回滾的異常類陣列
noRollbackForClassName 類名陣列,必須繼承自Throwable 不會導致事務回滾的異常類名字陣列

用法

@Transactional 可以作用於介面、介面方法、類以及類方法上。當作用於類上時,該類的所有 public 方法將都具有該型別的事務屬性,同時,我們也可以在方法級別使用該標註來覆蓋類級別的定義。

雖然 @Transactional 註解可以作用於介面、介面方法、類以及類方法上,但是 Spring 建議不要在介面或者介面方法上使用該註解,因為這隻有在使用基於介面的代理時它才會生效。另外, @Transactional 註解應該只被應用到 public 方法上,這是由 Spring AOP 的本質決定的。如果你在 protected、private 或者預設可見性的方法上使用 @Transactional 註解,這將被忽略,也不會丟擲任何異常。

預設情況下,只有來自外部的方法呼叫才會被AOP代理捕獲,也就是,類內部方法呼叫本類內部的其他方法並不會引起事務行為,即使被呼叫方法使用@Transactional註解進行修飾。

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
 
  public Foo getFoo(String fooName) {
    // do something
  }
 
  // these settings have precedence for this method
  //方法上註解屬性會覆蓋類註解上的相同屬性
  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  public void updateFoo(Foo foo) {
    // do something
  }
}

自動提交(AutoCommit)與連線關閉時的是否自動提交

自動提交

預設情況下,資料庫處於自動提交模式。每一條語句處於一個單獨的事務中,在這條語句執行完畢時,如果執行成功則隱式的提交事務,如果
執行失敗則隱式的回滾事務。

對於正常的事務管理,是一組相關的操作處於一個事務之中,因此必須關閉資料庫的自動提交模式。不過,這個我們不用擔心,spring會將底層連線的自動提交特性設定為false。
org/springframework/jdbc/datasource/DataSourceTransactionManager.java

// switch to manual commit if necessary. this is very expensive in some jdbc drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getautocommit()) {
    txobject.setmustrestoreautocommit(true);
    if (logger.isdebugenabled()) {
        logger.debug("switching jdbc connection [" + con + "] to manual commit");
    }
    con.setautocommit(false);
}

有些資料連線池提供了關閉事務自動提交的設定,最好在設定連線池時就將其關閉。但C3P0沒有提供這一特性,只能依靠spring來設定。
因為JDBC規範規定,當連線物件建立時應該處於自動提交模式,這是跨DBMS的預設值,如果需要,必須顯式的關閉自動提交。C3P0遵守這一規範,讓客戶程式碼來顯式的設定需要的提交模式。

連線關閉時的是否自動提交

當一個連線關閉時,如果有未提交的事務應該如何處理?JDBC規範沒有提及,C3P0預設的策略是回滾任何未提交的事務。這是一個正確的策略,但JDBC驅動提供商之間對此問題並沒有達成一致。
C3P0的autoCommitOnClose屬性預設是false,沒有十分必要不要動它。或者可以顯式的設定此屬性為false,這樣會更明確。

Spring事務配置的五種方式

Spring配置檔案中關於事務配置總是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的只是代理機制這部分。

DataSource、TransactionManager這兩部分只是會根據資料訪問方式有所變化,比如使用Hibernate進行資料訪問時,DataSource實際為SessionFactory,TransactionManager的實現為HibernateTransactionManager。

具體如下圖:

Spring事務配置 (2)

根據代理機制的不同,總結了五種Spring事務的配置方式,配置檔案如下:

第一種方式:每個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:context
="http://www.springframework.org/schema/context"
    xmlns:aop
="http://www.springframework.org/schema/aop"    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"
><bean id="sessionFactory" 
            class
="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><propertyname="configLocation" value="classpath:hibernate.cfg.xml"/><propertyname="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/></bean><!-- 定義事務管理器(宣告式的事務)--><bean id="transactionManager"
        class
="org.springframework.orm.hibernate3.HibernateTransactionManager"><propertyname="sessionFactory" ref="sessionFactory"/></bean><!-- 配置DAO--><bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"><propertyname="sessionFactory" ref="sessionFactory"/></bean><bean id="userDao" 
        class
="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><!-- 配置事務管理器--><propertyname="transactionManager" ref="transactionManager"/><propertyname="target" ref="userDaoTarget"/><propertyname="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao"/><!-- 配置事務屬性--><propertyname="transactionAttributes"><props><propkey="*">PROPAGATION_REQUIRED</prop></props></property></bean></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:context
="http://www.springframework.org/schema/context"
    xmlns:aop
="http://www.springframework.org/schema/aop"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"
><bean id="sessionFactory" 
            class
="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><propertyname="configLocation" value="classpath:hibernate.cfg.xml"/><propertyname="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/></bean><!-- 定義事務管理器(宣告式的事務)--><bean id="transactionManager"
        class
="org.springframework.orm.hibernate3.HibernateTransactionManager"><propertyname="sessionFactory" ref="sessionFactory"/></bean><beanid="transactionBase" 
            class
="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" 
            lazy-init
="true" abstract="true"><!-- 配置事務管理器--><propertyname="transactionManager" ref="transactionManager"/><!-- 配置事務屬性--><propertyname="transactionAttributes"><props><propkey="*">PROPAGATION_REQUIRED</prop></props></property></bean><!-- 配置DAO--><bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"><propertyname="sessionFactory" ref="sessionFactory"/></bean><beanid="userDao" parent="transactionBase"><propertyname="target" ref="userDaoTarget"/></bean></beans>

第三種方式:使用攔截器

<?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:context
="http://www.springframework.org/schema/context"
    xmlns:aop
="http://www.springframework.org/schema/aop"    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"
><bean id="sessionFactory" 
            class
="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><propertyname="configLocation" value="classpath:hibernate.cfg.xml"/><propertyname="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/></bean><!-- 定義事務管理器(宣告式的事務)--><bean id="transactionManager"
        class
="org.springframework.orm.hibernate3.HibernateTransactionManager"><propertyname="sessionFactory" ref="sessionFactory"/></bean><beanid="transactionInterceptor" 
        class
="org.springframework.transaction.interceptor.TransactionInterceptor"><propertyname="transactionManager" ref="transactionManager"/><!-- 配置事務屬性--><propertyname="transactionAttributes"><props><propkey="*">PROPAGATION_REQUIRED</prop></props></property></bean><bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"><propertyname="beanNames"><list><value>*Dao</value></list></property><propertyname="interceptorNames"><list><value>transactionInterceptor</value></list></property></bean><!-- 配置DAO--><bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl"><propertyname="sessionFactory" ref="sessionFactory"/></bean></beans>

第四種方式:使用tx標籤配置的攔截器

<?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:context
="http://www.springframework.org/schema/context"
    xmlns:aop
="http://www.springframework.org/schema/aop"
    xmlns:tx
="http://www.springframework.org/schema/tx"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
><context:annotation-config/><context:component-scanbase-package="com.bluesky"/><bean id="sessionFactory" 
            class
="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><propertyname="configLocation" value="classpath:hibernate.cfg.xml"/><propertyname="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/></bean><!-- 定義事務管理器(宣告式的事務)--><bean id="transactionManager"
        class
="org.springframework.orm.hibernate3.HibernateTransactionManager"><propertyname="sessionFactory" ref="sessionFactory"/></bean><tx:adviceid="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:methodname="*" propagation="REQUIRED"/></tx:attributes></tx:advice><aop:config><aop:pointcutid="interceptorPointCuts"
            expression
="execution(* com.bluesky.spring.dao.*.*(..))"/><aop:advisoradvice-ref="txAdvice"
            pointcut-ref
="interceptorPointCuts"/></aop:config></beans>

第五種方式:全註解

<?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:context
="http://www.springframework.org/schema/context"
    xmlns:aop
="http://www.springframework.org/schema/aop"
    xmlns:tx
="http://www.springframework.org/schema/tx"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
><context:annotation-config/><context:component-scanbase-package="com.bluesky"/><tx:annotation-driventransaction-manager="transactionManager"/><bean id="sessionFactory" 
            class
="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><propertyname="configLocation" value="classpath:hibernate.cfg.xml"/><propertyname="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/></bean><!-- 定義事務管理器(宣告式的事務)--><bean id="transactionManager"
        class
="org.springframework.orm.hibernate3.HibernateTransactionManager"><propertyname="sessionFactory" ref="sessionFactory"/></bean></beans>

此時在DAO上需加上@Transactional註解,如下:

package com.bluesky.spring.dao;

import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Component;

import com.bluesky.spring.domain.User;

@Transactional
@Component(
"userDao")
publicclass UserDaoImplextends HibernateDaoSupportimplements UserDao {

   
public List<User> listUsers() {
       
returnthis.getSession().createQuery("from User").list();
    }
   
   
}

關於攔截方法呼叫其他內部方法無法被攔截問題的解決

攔截器的實現原理很簡單,就是動態代理,實現AOP機制。當外部呼叫被攔截bean的攔截方法時,可以選擇在攔截之前或者之後等條件執行攔截方法之外的邏輯,比如特殊許可權驗證,引數修正等操作。但是如果現在一個需求是,當外部呼叫攔截bean的時候,不但要執行攔截當前方法,如果當前方法內部同時呼叫了其他內部方法,也要被攔截。按照目前的攔截器實現邏輯,是無法攔截當前方法內部呼叫的方法的,這樣說有點抽象,看一個程式碼:

public class BeanA{  
public void method1(){  
method2();  
}  
public void method2(){  
...  
}  
}

當外部呼叫beanA.method1();的時候,攔截器執行攔截邏輯,執行完畢後進入method1方法執行,當呼叫method2的時候,攔截器是否會再次攔截?是不會的,這裡涉及到攔截器的一個原理,攔截器涉及兩個物件,代理物件和原始物件,攔截器所執行的代理物件執行完畢後,當執行method1即進入了原始物件,那麼在原始物件中呼叫method2,是無法進行攔截的。所以很顯眼,這樣無法滿足我們的需求。

在spring的原始碼中通過一個增強物件的檢查,控制了當前的內部呼叫是否使用代理來執行,這讓人感到無奈。spring的作者們很隱晦的提出避免內部呼叫的方法。

我們可能會想,在外部呼叫兩次beanA,第一次呼叫method1,第二次呼叫method2,這樣做可以解決問題,但是這樣的直接後果是我們的邏輯程式碼將變得紊亂,並非所有的場景下都可以通過這樣的設計來完成。雖然這是spring官方推薦的避免內部呼叫的idea。

查看了相關資料,得到了一種方法,即在method1的內部,通過直接獲取當前代理物件的方式然後通過代理物件呼叫method2,這樣觸發攔截。

看看程式碼:

    public void method1(){  
        logger.error("1");  
          
        // 如果希望呼叫的內部方法也被攔截,那麼必須用過上下文獲取代理物件執行呼叫,而不能直接內部呼叫,否則無法攔截  
        if(null != AopContext.currentProxy()){  
            ((NorQuickNewsDAO)AopContext.currentProxy()).method2();  
        }else{  
            method2();  
        }         
    }  
      
    public void method2(){  
        logger.error("2");  
    }

我們顯示的呼叫了AopContext來獲取當前代理物件,然後呼叫其方法,這樣做還必須的一個步驟是將當前的代理暴露給執行緒使用,在配置檔案中需要配置一個引數:

 <property name="exposeProxy">  
       <value>true</value>  
 </property>

它是ProxyConfig的一個引數,預設是false,如果不設定這個引數,那麼上述java程式碼將無法獲取當前執行緒中的代理物件。

這種方法可以成功觸發攔截,但是也帶來了其他問題,比如程式碼的織入,我們的程式碼將變得複雜而且晦澀,而且嚴格要求系統針對於當前的bean必須配置攔截器,否則會因為找不到攔截器而丟擲異常。

這樣做有什麼負面影響?對事務的影響,對安全的影響,現在不得而知,還需要逐步去測試以嘗試。