Spring的AOP與代理

spring 支持兩種注入方式:
setter/constructor
支持多種配置方式:
xml/java5註解/java類配置
支持兩種事務管理:
聲明性/編程性
實際上上述方式只有一個就能保證系統構建與運行了,就是說它們都是可以互換的。當然每個方式的優缺點不同。

6.2  Spring的AOP
AOP(Aspect Orient Programming),也就是面向切面編程,作爲面向對象編程的一種補充。問世的時間並不太長,甚至在國內的翻譯還不太統一(有些書翻譯成面向方面編程),但它確實極好地補充了面向對象編程的方式。面向對象編程將程序分解成各個層次的對象,而面向切面編程將程序運行過程分解成各個切面。

可以這樣理解,面向對象編程是從靜態角度考慮程序結構,面向切面編程是從動態角度考慮程序運行過程。

Spring AOP是Spring框架的一個重要組件,極好地補充了Spring IoC容器的功能。Spring AOP將Spring IoC容器與AOP組件緊密結合,豐富了IoC容器的功能。當然,即使不使用AOP組件,依然可以使用Spring的IoC容器。

6.2.1  AOP的基本概念
AOP從程序運行角度考慮程序的流程,提取業務處理過程的切面。AOP面向的是程序運行中各個步驟,希望以更好的方式來組合業務處理的各個步驟。

AOP框架並不與特定的代碼耦合,AOP框架能處理程序執行中的特定點,而不是某個具體的程序。AOP框架具有如下兩個特徵:

  ● 各步驟之間的良好隔離性。

  ● 源代碼無關性。

下面是關於面向切面編程的一些術語:

  ● 切面,業務流程運行的某個特定步驟,就是運行過程的關注點,關注點可能橫切多個對象。

  ● 連接點,程序執行過程中明確的點,如方法的調用或異常的拋出。Spring AOP中,連接點總是方法的調用,Spring並沒有顯式地使用連接點。

  ● 處理(Advice),AOP框架在特定的連接點執行的動作。處理有around、before和throws等類型。大部分框架都以攔截器作爲處理模型。

  ● 切入點,系列連接點的集合,它確定處理觸發的時機。AOP框架允許開發者自己定義切入點,如使用正則表達式。

  ● 引入,添加方法或字段到被處理的類。Spring允許引入新的接口到任何被處理的對象。例如,可以使用一個引入,使任何對象實現IsModified接口,以此來簡化緩存。

  ● 目標對象,包含連接點的對象。也稱爲被處理對象或被代理對象。

  ● AOP代理,AOP框架創建的對象,包含處理。簡單地說,代理就是對目標對象的加強。Spring中的AOP代理可以是JDK動態代理,也可以是CGLIB代理。前者爲實現接口的目標對象的代理,後者爲不實現接口的目標對象的代理。

注意:面向切面編程是比較前沿的知識,而國內大部分翻譯人士翻譯計算機文獻時,總是一邊開着各種詞典和翻譯軟件,一邊逐詞去看文獻,不是先從總體上把握知識的架構。因此,難免導致一些術語的翻譯詞不達意,例如,Socket被翻譯成“套接字”等。在面向切面編程的各術語翻譯上,也存在較大的差異。對於Advice一詞,有翻譯爲“通知”的,有翻譯爲“建議”的,如此種種,不一而足。實際上,Advice指AOP框架在特定切面所做的事情,故而筆者翻譯爲“處理”,希望可以表達Advice的真正含義。

6.2.2  AOP的代理
所謂AOP代理,就是AOP框架動態創建的對象,這個對象通常可以作爲目標對象的替代品,而AOP代理提供比目標對象更加強大的功能。真實的情形是,當應用調用AOP代理的方法時,AOP代理會在自己的方法中回調目標對象的方法,從而完成應用的調用。

關於AOP代理的典型例子就是Spring中的事務代理Bean。通常,目標Bean的方法不是事務性的,而AOP代理包含目標Bean的全部方法,而且這些方法經過加強變成了事務性方法。簡單地說,目標對象是藍本,AOP代理是目標對象的加強,在目標對象的基礎上,增加屬性和方法,提供更強大的功能。

目標對象包含一系列切入點。切入點可以觸發處理連接點集合。用戶可以自己定義切入點,如使用正則表達式。AOP代理包裝目標對象,在切入點處加入處理。在切入點加入的處理,使得目標對象的方法功能更強。

Spring默認使用JDK動態代理實現AOP代理,主要用於代理接口。也可以使用CGLIB代理。實現類的代理,而不是接口。如果業務對象沒有實現接口,默認使用CGLIB代理。但面向接口編程是良好的習慣,儘量不要面向具體類編程。因此,業務對象通常應實現一個或多個接口。

下面是一個簡單動態代理模式的示例,首先有一個Dog的接口,接口如下:

public interface Dog

{

    //info方法聲明

    public void info();

    //run方法聲明

    public void run();

}

然後,給出該接口的實現類,實現類必須實現兩個方法,源代碼如下:

public class DogImpl implements Dog

{

    //info方法實現,僅僅打印一個字符串

    public void info()

    {

        System.out.println("我是一隻獵狗");

    }

    //run方法實現,僅僅打印一個字符串

    public void run()

    {

        System.out.println("我奔跑迅速");

    }

}

上面的代碼沒有絲毫獨特之處,是典型的面向接口編程的模型,爲了有更好的解耦,採用工廠來創建Dog實例。工廠源代碼如下:

public class DogFactory

{

    //工廠本身是單態模式,因此,將DogFactory作爲靜態成員變量保存

    private static DogFactory df;

    //將Dog實例緩存

    private Dog gundog;

    //默認的構造器,單態模式需要的構造器是private

    private DogFactory()

    {

    }

    //單態模式所需的靜態方法,該方法是創建本類實例的唯一方法點

    public static DogFactory instance()

    {

        if (df == null)

        {

              df = new DogFactory();

        }

        return df;

    }

    //獲得Dog實例

    public Dog getDog(String dogName)

    {

        //根據字符串參數決定返回的實例

        if (dogName.equals("gundog"))

        {

            //返回Dog實例之前,先判斷緩存的Dog是否存在,如果不存在才創建,

            否則直接返回緩存的Dog實例

              if (gundog == null )

              {

                  gundog = new DogImpl();

              }

              return gundog;

         }

        return null;

    }

}

下面是一個通用的處理類,該處理類沒有與任何特定的類耦合,它可以處理所有的目標對象。從JDK 1.3起,Java的import java.lang.reflect下增加InvocationHandler接口,該接口是所有處理類的根接口。

該類處理類的源代碼如下:

public class ProxyHandler implements InvocationHandler

{

    //需被代理的目標對象

    private Object target;

    //執行代理的目標方法時,該invoke方法會被自動調用

    public Object invoke(Object proxy, Method method, Object[] args)throws
    Exception

    {

        Object result = null;

        if (method.getName().equals("info"))

        {

              System.out.println("======開始事務...");

              result =method.invoke(target, args);

              System.out.println("======提交事務...");

        }

        else

        {

              result =method.invoke(target, args);

        }

        return result;

    }

    //通過該方法,設置目標對象

    public void setTarget(Object o)

    {

        this.target = o;

    }

}

該處理類實現InvocationHandler接口,實現該接口必須實現invoke(Object proxy, Method method, Object[] args)方法,程序調用代理的目標方法時,自動變成調用invoke方法。

該處理類並未與任何接口或類耦合,它完全是通用的,它的目標實例是Object類型,可以是任何的類型。

在invoke方法內,對目標對象的info方法進行簡單加強,在開始執行目標對象的方法之前,先打印開始事務,執行目標對象的方法之後,打印提交事務。

通過method對象的invoke方法,可以完成目標對象的方法調用,執行代碼如下:

result =method.invoke(target, args);

下面是代理工廠:

public class MyProxyFactory

{

    /**

      * 實例Service對象

      * @param serviceName String

      * @return Object

      */

    public static Object getProxy(Object object)

    {

         //代理的處理類

        ProxyHandler handler = new ProxyHandler();

         //把該dog實例託付給代理操作

        handler.setTarget(object);

        //第一個參數是用來創建動態代理的ClassLoader對象,只要該對象能訪問Dog接口
        即可

        //第二個參數是接口數組,正是代理該接口數組

        //第三個參數是代理包含的處理實例

        return Proxy.newProxyInstance(DogImpl.class.getClassLoader(),

            object.getClass().getInterfaces(),handler);

    }

}

代理工廠裏有一行代碼:

Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),handler);

Proxy.newProxyInstance()方法根據接口數組動態創建代理類實例,接口數組通過object.getClass().getInterfaces()方法獲得,創建的代理類是JVM在內存中動態創建的,該類實現傳入接口數組的全部接口。

因此,Dynamic Proxy要求被代理的必須是接口的實現類,否則無法爲其構造相應的動態類。因此,Spring對接口實現類採用Dynamic Proxy實現AOP,而對沒有實現任何接口的類,則通過CGLIB實現AOP代理。

下面是主程序:

public class TestDog

{

    public static void main(String[] args)

    {

        Dog dog = null;

        //創建Dog實例,該實例將作爲被代理對象

        Dog targetObject = DogFactory.instance().getDog("gundog");

        //以目標對象創建代理

        Object proxy = MyProxyFactory.getProxy(targetObject);

        if (proxy instanceof Dog)

        {

              dog = (Dog)proxy;

        }

        //測試代理的方法

        dog.info();

        dog.run();

    }

}

代理實例會實現目標對象實現的全部接口。因此,代理實例也實現了Dog接口,程序運行結果如下:

[java] ======開始事務...

[java] 我是一隻獵狗

[java] ======提交事務...

[java] 我奔跑迅速

代理實例加強了目標對象的方法——僅僅打印了兩行字符串。當然,此種加強沒有實際意義。試想一下,若程序中打印字符串的地方,換成真實的事務開始和事務提交,則代理實例的方法爲目標對象的方法增加了事務性。

6.2.3  創建AOP代理
通過前面的介紹,AOP代理就是由AOP框架動態生成的一個對象,該對象可作爲目標對象使用,AOP代理包含了目標對象的全部方法。但AOP代理中的方法與目標對象的方法存在差異:AOP方法在特定切面插入處理,在處理之間回調目標對象的方法。

AOP代理所包含的方法與目標對象所包含的方法的示意圖,如圖6.1所示。

Spring中AOP代理由Spring的IoC容器負責生成和管理,其依賴關係也由IoC容器負責管理。因此,AOP代理能夠引用容器中的其他Bean實例,這種引用由IoC容器的依賴注入提供。

Spring的AOP代理大都由ProxyFactoryBean工廠類產生,如圖6.1所示,產生一個AOP代理至少有兩個部分:目標對象和AOP框架所加入的處理。因此,配置ProxyFactoryBean時需要確定如下兩個屬性:

  ● 代理的目標對象。

  ● 處理(Advice)。

注意:關於Advice的更多知識,此處由於篇幅原因,無法深入討論。實際上,Spring也提供了很多種Advice的實現,如需要更深入瞭解Spring AOP,請讀者參考筆者所著的《Spring2.0寶典》。

所有代理工廠類的父類是org.springframework.aop.framework.ProxyConfig。因此,該類的屬性是所有代理工廠的共同屬性,這些屬性也是非常關鍵的,包括:

  ● proxyTargetClass,確定是否代理目標類,如果需要代理目標是類,該屬性設爲true,此時需要使用CGLIB生成代理;如果代理目標是接口,該屬性設爲false,默認是false。

  ● optimize,確定是否使用強優化來創建代理。該屬性僅對CGLIB代理有效;對JDK 動態代理無效。

  ● frozen,確定是否禁止改變處理,默認是false。

  ● exposeProxy,代理是否可以通過ThreadLocal訪問,如果exposeProxy屬性爲true,則可通過AopContext.currentProxy()方法獲得代理。

  ● aopProxyFactory,所使用的AopProxyFactory具體實現。該參數用來指定使用動態代理、CGLIB或其他代理策略。默認選擇動態代理或CGLIB。一般不需要指定該屬性,除非需要使用新的代理類型,才指定該屬性。

配置ProxyFactoryBean工廠bean時,還需要指定它的特定屬性,ProxyFactoryBean的特定屬性如下所示:

  ● proxyInterfaces,接口名的字符串數組。如果沒有確定該參數,默認使用CGLIB代理。

  ● interceptorNames,處理名的字符串數組。此處的次序很重要,排在前面的處理,優先被調用。此處的處理名,只能是當前工廠中處理的名稱,而不能使用bean引用。處理名字支持使用通配符(*)。

  ● singleton,工廠是否返回單態代理。默認是true,無論 getObject()被調用多少次,將返回相同的代理實例。如果需要使用有狀態的處理——例如,有狀態的mixin,可改變默認設置,prototype處理。

6.2.4  代理接口
當目標Bean的實現類實現了接口後,Spring AOP可以爲其創建JDK動態代理,而無須使用CGLIB創建的代理,這種代理稱爲代理接口。

創建AOP代理必須指定兩個屬性:目標Bean和處理。實際上,很多AOP框架都以攔截器作爲處理。因爲Spring AOP與IoC容器的良好整合,因此配置代理Bean時,完全可以利用依賴注入來管理目標Bean和攔截器Bean。

下面的示例演示了基於AOP的權限認證,它是簡單的TestService接口,該接口模擬Service組件,該組件內包含兩個方法:

  ● 查看數據。

  ● 修改數據。

接口的源代碼如下:

//Service組件接口

public interface TestService

{

    //查看數據

    void view();

    //修改數據

    void modify();

}

該接口的實現類實現兩個方法。因爲篇幅限制,本示例並未顯示出完整的查看數據和修改數據的持久層操作,僅僅在控制檯打印兩行信息。實際的項目實現中,兩個方法的實現則改成對持久層組件的調用,這不會影響示例程序的效果。實現類的源代碼如下:

TestService接口的實現類

public class TestServiceImpl implements TestService

{

    //實現接口必須實現的方法

    public void view()

    {

        System.out.println("用戶查看數據");

    }

    //實現接口必須實現的方法

    public void modify()

    {

        System.out.println("用戶修改數據");

    }

}

示例程序採用Around 處理作爲攔截器,攔截器中使用依賴注入獲得當前用戶名。實際Web應用中,用戶應該從session中讀取。這不會影響示例代碼的效果。攔截器源代碼如下:

public class AuthorityInterceptor implements MethodInterceptor

{

    //當前用戶名

    private String user;

    //依賴注入所必需的setter方法

    public void setUser(String user)

    {

        this.user = user;

    }

    public Object invoke(MethodInvocation invocation) throws Throwable

    {

        //獲取當前攔截的方法名

        String methodName = invocation.getMethod().getName();

        //下面執行權限檢查

        //對既不是管理員,也不是註冊用戶的情況

        if (!user.equals("admin") && !user.equals("registedUser"))

        {

              System.out.println("您無權執行該方法");

              return null;

        }

        //對僅僅是註冊用戶,調用修改數據的情況

        else if (user.equals("registedUser") && methodName.equals
        ("modify"))

        {

              System.out.println("您不是管理員,無法修改數據");

              return null;

        }

        //對管理員或註冊用戶,查看數據的情況

        else

        {

              return invocation.proceed();

        }

    }

}

TestAction類依賴TestService。因篇幅關係,此處不給出TestAction的接口的源代碼,TestActionImpl的源代碼如下:

public class TestActionImpl

{

    //將TestService作爲成員變量,面向接口編程

    private TestService ts;

    //依賴注入的setter方法

    public void setTs(TestService ts)

    {

        this.ts = ts;

    }

    //修改數據

    public void modify()

    {

        ts.modify();

    }

    //查看數據

    public void view()

    {

        ts.view();

    }

}

配置文件如下:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置目標Bean -->

    <bean id="serviceTarget" class="lee.TestServiceImpl"/>

    <!-- 配置攔截器,攔截器作爲處理使用 -->

    <bean id="authorityInterceptor" class="lee.AuthorityInterceptor">

        <property name="user" value="admin"/>

    </bean>

    <!-- 配置代理工廠Bean,負責生成AOP代理 -->

    <bean id="service" class="org.springframework.aop.framework.
    ProxyFactoryBean">

        <!-- 指定AOP代理所實現的接口 -->

        <property name="proxyInterfaces" value="lee.TestService"/>

        <!-- 指定AOP代理所代理的目標Bean -->

        <property name="target" ref="serviceTarget"/>

        <!-- AOP代理所需要的攔截器列表 -->

        <property name="interceptorNames">

            <list>

                <value>authorityInterceptor</value>

            </list>

        </property>

    </bean>

    <!--  配置Action Bean,該Action依賴TestService Bean -->

    <bean id="testAction" class="lee.TestActionImpl">

        <!-- 此處注入的是依賴代理Bean -->

        <property name="ts" ref="service"/>

    </bean>

</beans>

主程序請求testAction Bean,然後調用該Bean的兩個方法,主程序如下:

public class BeanTest

{

    public static void main(String[] args)throws Exception

    {

        //創建Spring容器實例

        ApplicationContext ctx = new FileSystemXmlApplicationContext
        ("bean.xml");

        //獲得TestAction bean

        TestAction ta = (TestAction)ctx.getBean("testAction");

        //調用bean的兩個測試方法

        ta.view();

        ta.modify();

    }

}

程序執行結果如下:

[java] 用戶查看數據

[java] 用戶修改數據

代理似乎沒有發揮任何作用。因爲配置文件中的當前用戶是admin,admin用戶具備訪問和修改數據的權限,因此代理並未阻止訪問。將配置文件中的admin修改成registed- User,再次執行程序,得到如下結果:

[java] 用戶查看數據

[java] 您不是管理員,無法修改數據

代理阻止了registedUser修改數據,查看數據可以執行。將registedUser修改成其他用戶,執行程序,看到如下結果:

[java] 您無權執行該方法

[java] 您無權執行該方法

代理阻止用戶對兩個方法的執行。基於AOP的權限檢查,可以降低程序的代碼量,因爲無須每次調用方法之前,手動編寫權限檢查代碼;同時,權限檢查與業務邏輯分離,提高了程序的解耦。

示例中的目標Bean被暴露在容器中,可以被客戶端代碼直接訪問。爲了避免客戶端代碼直接訪問目標Bean,可以將目標Bean定義成代理工廠的嵌套Bean,修改後的配置文件如下:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring配置文件的根元素,以及Spring配置文件的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置攔截器,攔截器作爲處理使用 -->

    <bean id="authorityInterceptor" class="lee.AuthorityInterceptor">

        <property name="user" value="admin"/>

    </bean>

    <!-- 配置代理工廠Bean,該工廠Bean將負責創建目標Bean的代理 -->

    <bean id="service" class="org.springframework.aop.framework.
    ProxyFactoryBean">

        <!-- 指定AOP代理所實現的接口 -->

        <property name="proxyInterfaces" value="lee.TestService"/>

        <property name="target">

            <!-- 以嵌套Bean的形式定義目標Bean,避免客戶端直接訪問目標Bean -->

            <bean class="lee.TestServiceImpl"/>

        </property>

        <!-- AOP代理所需要的攔截器列表 -->

        <property name="interceptorNames">

            <list>

                <value>authorityInterceptor</value>

            </list>

        </property>

    </bean>

    <!--  配置Action Bean,該Action依賴TestService Bean -->

    <bean id="testAction" class="lee.TestActionImpl">

        <!-- 此處注入的是依賴代理Bean -->

        <property name="ts" ref="service"/>

    </bean>

</beans>

由上面介紹的內容可見,Spring的AOP是對JDK動態代理模式的深化。通過Spring AOP組件,允許通過配置文件管理目標Bean和AOP所需的處理。

下面將繼續介紹如何爲沒有實現接口的目標Bean創建CGLIB代理。

6.2.5  代理類
如果目標類沒有實現接口,則無法創建JDK動態代理,只能創建CGLIB代理。如果需要沒有實現接口的Bean實例生成代理,配置文件中應該修改如下兩項:

  ● 去掉<property name="proxyInterfaces"/>聲明。因爲不再代理接口,因此,此處的配置沒有意義。

  ● 增加<property name="proxyTargetClass">子元素,並設其值爲true,通過該元素強制使用CGLIB代理,而不是JDK動態代理。

注意:最好面向接口編程,不要面向類編程。同時,即使在實現接口的情況下,也可強制使用CGLIB代理。

CGLIB代理在運行期間產生目標對象的子類,該子類通過裝飾器設計模式加入到Advice中。因爲CGLIB代理是目標對象的子類,則必須考慮保證如下兩點:

  ● 目標類不能聲明成final,因爲final類不能被繼承,無法生成代理。

  ● 目標方法也不能聲明成final,final方法不能被重寫,無法得到處理。

當然,爲了需要使用CGLIB代理,應用中應添加CGLIB二進制Jar文件。

6.2.6  使用BeanNameAutoProxyCreator自動創建代理
這是一種自動創建事務代理的方式,一旦在容器中配置了BeanNameAutoProxyCreator實例,該實例將會對指定名字的Bean實例自動創建代理。實際上,BeanNameAutoProxyCreator是一個Bean後處理器,理論上它會對容器中所有的Bean進行處理,實際上它只對指定名字的Bean實例創建代理。

BeanNameAutoProxyCreator根據名字自動生成事務代理,名字匹配支持通配符。

與ProxyFactoryBean一樣,BeanNameAutoProxyCreator需要一個interceptorNames屬性,該屬性名雖然是“攔截器”,但並不需要指定攔截器列表,它可以是Advisor或任何處理類型。

下面是使用BeanNameAutoProxyCreator的配置片段:

<!-- 定義事務攔截器bean -->

<bean id="transactionInterceptor"

    class="org.springframework.transaction.interceptor.
    TransactionInterceptor">

    <!--  事務攔截器bean需要依賴注入一個事務管理器 -->

    <property name="transactionManager" ref="transactionManager"/>

    <property name="transactionAttributes">

        <!--  下面定義事務傳播屬性 -->

        <props>

            <prop key="insert*">PROPAGATION_REQUIRED </prop>

            <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

            <prop key="*">PROPAGATION_REQUIRED</prop>

        </props>

    </property>

    </bean>

<!-- 定義BeanNameAutoProxyCreator Bean,它是一個Bean後處理器,

        負責爲容器中特定的Bean創建AOP代理 -->

<bean class="org.springframework.aop.framework.autoproxy.
    BeanNameAutoProxyCreator">

    <!-- 指定對滿足哪些bean name的bean自動生成業務代理 -->

    <property name="beanNames">

        <list>

            <!-- 下面是所有需要自動創建事務代理的Bean -->

            <value>core-services-applicationControllerSevice</value>

            <value>core-services-deviceService</value>

            <value>core-services-authenticationService</value>

            <value>core-services-packagingMessageHandler</value>

            <value>core-services-sendEmail</value>

            <value>core-services-userService</value>

            <!-- 此處可增加其他需要自動創建事務代理的Bean -->

        </list>

    </property>

    <!-- 下面定義BeanNameAutoProxyCreator所需的攔截器 -->

    <property name="interceptorNames">

        <list>

            <value>transactionInterceptor</value>

              <!-- 此處可增加其他新的Interceptor -->

        </list>

    </property>

</bean>

上面的片段是使用BeanNameAutoProxyCreator自動創建事務代理的片段。Transaction- Interceptor用來定義事務攔截器,定義事務攔截器時傳入事務管理器Bean,也指定事務傳播屬性。

通過BeanNameAutoProxyCreator定義Bean後處理器,定義該Bean後處理器時,通過beanNames屬性指定有哪些目標Bean生成事務代理;還需要指定“攔截器鏈”,該攔截器鏈可以由任何處理組成。

定義目標Bean還可使用通配符,使用通配符的配置片段如下所示:

<!-- 定義BeanNameAutoProxyCreator Bean,它是一個Bean後處理器,

        負責爲容器中特定的Bean創建AOP代理 -->

<bean class="org.springframework.aop.framework.autoproxy.
BeanNameAutoProxyCreator">

    <!-- 指定對滿足哪些bean name的bean自動生成業務代理 -->

    <property name="beanNames">

        <!-- 此處使用通配符確定目標bean -->

        <value>*DAO,*Service,*Manager</value>

    </property>

    <!-- 下面定義BeanNameAutoProxyCreator所需的事務攔截器 -->

    <property name="interceptorNames">

        <list>

            <value>transactionInterceptor</value>

              <!-- 此處可增加其他新的Interceptor -->

        </list>

    </property>

</bean>

上面的配置片段中,所有名字以DAO、Service、Manager結尾的bean,將由該“bean後處理器”爲其創建事務代理。目標bean不再存在,取而代之的是目標bean的事務代理。通過這種方式,不僅可以極大地降低配置文件的繁瑣,而且可以避免客戶端代碼直接調用目標bean。

注意:雖然上面的配置片段是爲目標對象自動生成事務代理。但這不是唯一的,如果有需要,可以爲目標對象生成任何的代理。BeanNameAutoProxyCreator爲目標對象生成怎樣的代理,取決於傳入怎樣的處理Bean,如果傳入事務攔截器,則生成事務代理Bean;否則將生成其他代理,在後面的實例部分,讀者將可以看到大量使用BeanNameAutoProxy- Creator創建的權限檢查代理。

6.2.7  使用DefaultAdvisorAutoProxyCreator自動創建代理
Spring還提供了另一個Bean後處理器,它也可爲容器中的Bean自動創建代理。相比之下,DefaultAdvisorAutoProxyCreator是更通用、更強大的自動代理生成器。它將自動應用於當前容器中的advisor,不需要在DefaultAdvisorAutoProxyCreator定義中指定目標Bean的名字字符串。

這種定義方式有助於配置的一致性,避免在自動代理創建器中重複配置目標Bean 名。

使用該機制包括:

  ● 配置DefaultAdvisorAutoProxyCreator bean定義。

  ● 配置任何數目的Advisor,必須是Advisor,不僅僅是攔截器或其他處理。因爲,必須使用切入點檢查處理是否符合候選Bean定義。

DefaultAdvisorAutoProxyCreator計算Advisor包含的切入點,檢查處理是否應該被應用到業務對象,這意味着任何數目的Advisor都可自動應用到業務對象。如果Advisor中沒有切入點符合業務對象的方法,這個對象就不會被代理。如果增加了新的業務對象,只要它們符合切入點定義,DefaultAdvisorAutoProxyCreator將自動爲其生成代理,無須額外   配置。

當有大量的業務對象需要採用相同處理,DefaultAdvisorAutoProxyCreator是非常有用的。一旦定義恰當,直接增加業務對象,而不需要額外的代理配置,系統自動爲其增加     代理。

<beans>

    <!-- 定義Hibernate局部事務管理器,可切換到JTA全局事務管理器 -->

    <bean id="transactionManager"

         class="org.springframework.orm.hibernate3.
        HibernateTransactionManager">

        <!-- 定義事務管理器時,依賴注入SessionFactory -->

         <property name="sessionFactory" ref bean="sessionFactory"/>

    </bean>

    <!-- 定義事務攔截器 -->

    <bean id="transactionInterceptor"

        class="org.springframework.transaction.interceptor.
        TransactionInterceptor">

         <property name="transactionManager" ref="transactionManager"/>

         <property name="transactionAttributeSource">

              <props>

                  <prop key="*">PROPAGATION_REQUIRED</prop>

                  <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

              </props>

        </property>

    </bean>

    <!-- 定義DefaultAdvisorAutoProxyCreator  Bean,這是一個Bean後處理器 -->

    <bean class="org.springframework.aop.framework.autoproxy.
    DefaultAdvisorAutoProxyCreator"/>

    <!-- 定義事務Advisor -->

    <bean class="org.springframework.transaction.interceptor.
    TransactionAttributeSourceAdvisor">

         <property name="transactionInterceptor" ref=
        "transactionInterceptor"/>

    </bean>

    <!-- 定義額外的Advisor>

    <bean id="customAdvisor" class="lee.MyAdvisor"/>

</beans>

DefaultAdvisorAutoProxyCreator支持過濾和排序。如果需要排序,可讓Advisor實現org.springframework.core.Ordered接口來確定順序。TransactionAttributeSourceAdvisor已經實現Ordered接口,因此可配置其順序,默認是不排序。

採用這樣的方式,一樣可以避免客戶端代碼直接訪問目標Bean,而且配置更加簡潔。只要目標Bean符合切入點檢查,Bean後處理器自動爲目標Bean創建代理,無須依次指定目標Bean名。

注意:Spring也支持以編程方式創建AOP代理,但這種方式將AOP代理所需要的目標對象和處理Bean等對象的耦合降低到代碼層次,因此不推薦使用

轉自:http://blog.csdn.net/gabriel80/archive/2008/05/23/2473872.aspx

發佈了46 篇原創文章 · 獲贊 1 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章