<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    OMG,到底在尋找什么..................
    (構(gòu)造一個完美的J2EE系統(tǒng)所需要的完整知識體系)
    posts - 198,  comments - 37,  trackbacks - 0

    原貼地址:http://book.csdn.net/bookfiles/111/1001113463.shtml

    一.AOP概念

    介紹完
    IoC 之后,我們來介紹另外一個重要的概念: AOP(Aspect Oriented Programming) ,也就是面向方面編程的技術(shù)。 AOP 基于 IoC 基礎(chǔ),是對 OOP 的有益補充。

    AOP 將應(yīng)用系統(tǒng)分為兩部分,核心業(yè)務(wù)邏輯 Core business concerns )及橫向的通用邏輯, 也就是所謂的方面 Crosscutting enterprise concerns ,例如,所有大中型應(yīng)用都要涉及到的持久化管理( Persistent )、事務(wù)管理( Transaction Management )、安全管理( Security )、 日志管理( Logging )和調(diào)試管理( Debugging )等。

    AOP 正在成為軟件開發(fā)的下一個光環(huán)。使用 AOP ,你可以將處理 aspect 的代碼注入主程序,通常主程序的主要目的并不在于處理這些 aspect AOP 可以防止代碼混亂。

    Spring framework 是很有前途的 AOP 技術(shù)。作為一種非侵略性的、輕型的 AOP framework ,你無需使用預(yù)編譯器或其他的元標簽,便可以在 Java 程序中使用它。這意味著開發(fā)團隊里只需一人要對付 AOP framework ,其他人還是像往常一樣編程。

    6.3.1? A OP 概念

    讓我們從定義一些重要的 AOP 概念開始。

    方面( Aspect ):一個關(guān)注點的模塊化,這個關(guān)注點實現(xiàn)可能另外橫切多個對象。事務(wù)管理是 J2EE 應(yīng)用中一個很好的橫切關(guān)注點例子。方面用 Spring Advisor 或攔截器實現(xiàn)。

    連接點( Joinpoint ):程序執(zhí)行過程中明確的點,如方法的調(diào)用或特定的異常被拋出。

    通知( Advice ):在特定的連接點, AOP 框架執(zhí)行的動作。各種類型的通知包括“ around ”、“ before ”和“ throws ”通知。通知類型將在下面討論。許多 AOP 框架包括 Spring 都是以攔截器做通知模型,維護一個“圍繞”連接點的攔截器鏈。

    切入點( Pointcut ):指定一個通知將被引發(fā)的一系列連接點的集合。 AOP 框架必須允許開發(fā)者指定切入點,例如,使用正則表達式。

    引入( Introduction ):添加方法或字段到被通知的類。 Spring 允許引入新的接口到任何被通知的對象。例如,你可以使用一個引入使任何對象實現(xiàn) IsModified 接口,來簡化緩存。

    目標對象( Target Object ):包含連接點的對象,也被稱作被通知或被代理對象。

    AOP 代理( AOP Proxy ): AOP 框架創(chuàng)建的對象,包含通知。在 Spring 中, AOP 代理可以是 JDK 動態(tài)代理或 CGLIB 代理。

    編織( Weaving ):組裝方面來創(chuàng)建一個被通知對象。這可以在編譯時完成(例如使用 AspectJ 編譯器),也可以在運行時完成。 Spring 和其他純 Java AOP 框架一樣,在運行時完成織入。

    各種通知類型包括:

    ? Around 通知:包圍一個連接點的通知,如方法調(diào)用。這是最強大的通知。 Aroud 通知在方法調(diào)用前后完成自定義的行為,它們負責(zé)選擇繼續(xù)執(zhí)行連接點或通過返回它們自己的返回值或拋出異常來短路執(zhí)行。

    ? Before 通知:在一個連接點之前執(zhí)行的通知,但這個通知不能阻止連接點前的執(zhí)行(除非它拋出一個異常)。

    ? Throws 通知:在方法拋出異常時執(zhí)行的通知。 Spring 提供強制類型的 Throws 通知,因此你可以書寫代碼捕獲感興趣的異常(和它的子類),不需要從 Throwable Exception 強制類型轉(zhuǎn)換。

    ? After returning 通知:在連接點正常完成后執(zhí)行的通知,例如,一個方法正常返回,沒有拋出異常。

    Around 通知是最通用的通知類型。大部分基于攔截的 AOP 框架(如 Nanning Jboss 4 只提供 Around 通知。

    如同 AspectJ Spring 提供所有類型的通知,我們推薦你使用最為合適的通知類型來實現(xiàn)需要的行為。例如,如果只是需要用一個方法的返回值來更新緩存,你最好實現(xiàn)一個 after returning 通知,而不是 around 通知,雖然 around 通知也能完成同樣的事情。使用最合適的通知類型使編程模型變得簡單,并能減少潛在錯誤。例如,你不需要調(diào)用在 around 通知中所需使用的 MethodInvocation proceed() 方法,因此就調(diào)用失敗。

    切入點的概念是 AOP 的關(guān)鍵,它使 AOP 區(qū)別于其他使用攔截的技術(shù)。切入點使通知獨立于 OO 的層次選定目標。例如,提供聲明式事務(wù)管理的 around 通知可以被應(yīng)用到跨越多個對象的一組方法上。 因此切入點構(gòu)成了 AOP 的結(jié)構(gòu)要素。

    下面讓我們實現(xiàn)一個 Spring AOP 的例子。在這個例子中,我們將實現(xiàn)一個 before advice ,這意味著 advice 的代碼在被調(diào)用的 public 方法開始前被執(zhí)行。以下是這個 before advice 的實現(xiàn)代碼。

    package com.ascenttech.springaop.test;
    import java.lang.reflect.Method;
    import org.springframework.aop.MethodBeforeAdvice;

    public class TestBeforeAdvice implements MethodBeforeAdvice {
    ???public void before(Method m, Object[] args, Object target) ?throws Throwable {
    ?????System.out.println("Hello world! (by " + this.getClass().getName() ?+ ")");
    ????}
    }

    接口 MethodBeforeAdvice 只有一個方法 before 需要實現(xiàn),它定義了 advice 的實現(xiàn)。 before 方法共用 3 個參數(shù),它們提供了相當(dāng)豐富的信息。參數(shù) Method m advice 開始后執(zhí)行的方法,方法名稱可以用作判斷是否執(zhí)行代碼的條件。 Object[] args 是傳給被調(diào)用的 public 方法的參數(shù)數(shù)組。當(dāng)需要記日志時,參數(shù) args 和被執(zhí)行方法的名稱都是非常有用的信息。你也可以改變傳給 m 的參數(shù),但要小心使用這個功能;編寫最初主程序的程序員并不知道主程序可能會和傳入?yún)?shù)的發(fā)生沖突。 Object target 是執(zhí)行方法 m 對象的引用。

    在下面的 BeanImpl 類中,每個 public 方法調(diào)用前,都會執(zhí)行 advice ,代碼如下。

    package com.ascenttech.springaop.test;
    public class BeanImpl implements Bean {

    ?public void theMethod() {
    ???
    ?System.out.println(this.getClass().getName() ? + "." + new Exception().getStackTrace()? [0].getMethodName???() ?+??"()"??+??"says HELLO!");????
    ?}
    }

    BeanImpl 實現(xiàn)了下面的接口 Bean ,代碼如下。

    package com.ascenttech.springaop.test;
    public interface Bean {
    ?public void theMethod();
    }

    雖然不是必須使用接口,但面向接口而不是面向?qū)崿F(xiàn)編程是良好的編程實踐, Spring 也鼓勵這樣做。

    pointcut advice 通過配置文件來實現(xiàn),因此,接下來你只需編寫主方法的 Java 代碼,代碼如下。

    package com.ascenttech.springaop.test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;

    public class Main {
    ?public static void main(String[] args) {

    ? //Read the configuration file
    ? ApplicationContext ctx = new FileSystemXmlApplicationContext("springconfig.xml");

    ? //Instantiate an object
    ? Bean x = (Bean) ctx.getBean("bean");

    ? //Execute the public method of the bean (the test)
    ? x.theMethod();
    ?}
    }

    我們從讀入和處理配置文件開始,接下來馬上要創(chuàng)建它。這個配置文件將作為粘合程序不同部分的“膠水”。讀入和處理配置文件后,我們會得到一個創(chuàng)建工廠 ctx ,任何一個 Spring 管理的對象都必須通過這個工廠來創(chuàng)建。對象通過工廠創(chuàng)建后便可正常使用。

    僅僅用配置文件便可把程序的每一部分組裝起來,代碼如下。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework. org/dtd/spring-beans.dtd">
    <beans>
    ?<!--CONFIG-->
    ?<bean id="bean" class="org.springframework.aop.framework.ProxyFactoryBean">
    ????<property name="proxyInterfaces">
    ?????????<value>com.ascenttech.springaop.test.Bean</value>
    ?????</property>

    ? <property name="target">
    ???
    ? <ref local="beanTarget"/>
    ? </property>

    ? <property name="interceptorNames">
    ?? <list>
    ??? <value>theAdvisor</value>
    ?? </list>
    ? </property>

    ?</bean>?

    ?<!--CLASS-->?
    ?<bean id="beanTarget" class="com.ascenttech.springaop.test.BeanImpl"/>
    ?<!--ADVISOR-->
    ?<!--Note: An advisor assembles pointcut and advice-->

    ?<bean id="theAdvisor" class="org.springframework.aop.support.RegexpMethod PointcutAdvisor">
    ? <property name="advice">
    ????? <ref local="theBeforeAdvice"/>
    ? </property>

    ? <property name="pattern">
    ???
    ?? <value>com\.ascenttech\.springaop\.test\.Bean\.theMethod</value>
    ? </property>

    ?</bean>
    ?<!--ADVICE-->

    ?<bean id="theBeforeAdvice" class="com.ascenttech.springaop.test.TestBefore Advice"/>
    </beans>

    4 bean 定義的次序并不重要。我們現(xiàn)在有了一個 advice 、一個包含了正則表達式 pointcut advisor 、一個主程序類和一個配置好的接口,通過工廠 ctx ,這個接口返回自己本身實現(xiàn)的一個引用。

    BeanImpl TestBeforeAdvice 都是直接配置。我們用一個惟一的 ID 創(chuàng)建一個 bean 元素,并指定了一個實現(xiàn)類,這就是全部的工作。

    advisor 通過 Spring framework 提供的一個 RegexMethodPointcutAdvisor 類來實現(xiàn)。我們用 advisor 的第一個屬性來指定它所需的 advice-bean ,第二個屬性則用正則表達式定義了 pointcut ,確保良好的性能和易讀性。

    最后配置的是 bean ,它可以通過一個工廠來創(chuàng)建。 bean 的定義看起來比實際上要復(fù)雜。 bean ProxyFactoryBean 的一個實現(xiàn),它是 Spring framework 的一部分。這個 bean 的行為通過以下的 3 個屬性來定義。

    屬性 proxyInterface 定義了接口類。

    屬性 target 指向本地配置的一個 bean ,這個 bean 返回一個接口的實現(xiàn)。

    屬性 interceptorNames 是惟一允許定義一個值列表的屬性,這個列表包含所有需要在 beanTarget 上執(zhí)行的 advisor 。注意, advisor 列表的次序是非常重要的。

    二.Spring的切入點,通知,advisor

    讓我們看看 Spring 如何處理切入點這個重要的概念。

    1 .概念

    Spring 的切入點模型能夠使切入點獨立于通知類型被重用。 同樣的切入點有可能接受不同的通知。

    org.springframework.aop.Pointcut 接口是重要的接口,用來指定通知到特定的類和方法目標,完整的接口定義如下。

    public interface Pointcut {
    ??? ClassFilter getClassFilter();
    ??? MethodMatcher getMethodMatcher();
    }

    Pointcut 接口分成兩個部分有利于重用類和方法的匹配部分,并且組合細粒度的操作(如和另一個方法匹配器執(zhí)行一個“并”的操作)。

    ClassFilter 接口被用來將切入點限制到一個給定的目標類的集合。如果 matches() 永遠返回 true ,所有的目標類都將被匹配。

    public interface ClassFilter {
    ?? boolean matches(Class clazz);
    }

    MethodMatcher 接口通常更加重要,完整的接口如下。

    public interface MethodMatcher {
    ??? boolean matches(Method m, Class targetClass);
    ??? boolean isRuntime();
    ??? boolean matches(Method m, Class targetClass, Object[] args);
    }

    matches(Method, Class) 方法被用來測試這個切入點是否匹配目標類的給定方法。這個測試可以在 AOP 代理創(chuàng)建的時候執(zhí)行,避免在所有方法調(diào)用時都需要進行測試。如果 2 個參數(shù)的匹配方法對某個方法返回 true ,并且 MethodMatcher isRuntime() 也返回 true ,那么 3 個參數(shù)的匹配方法將在每次方法調(diào)用的時候被調(diào)用。這使切入點能夠在目標通知被執(zhí)行之前立即查看傳遞給方法調(diào)用的參數(shù)。

    大部分 MethodMatcher 都是靜態(tài)的,意味著 isRuntime() 方法返回 false 。這種情況下, 3 個參數(shù)的匹配方法永遠不會被調(diào)用。

    如果可能,盡量使切入點是靜態(tài)的,使當(dāng) AOP 代理被創(chuàng)建時, AOP 框架能夠緩存切入點的測試結(jié)果。

    2 .切入點的運算

    Spring 支持的切入點的運算有

    并表示只要任何一個切入點匹配的方法。交表示兩個切入點都要匹配的方法。并通常比較有用。

    切入點可以用 org.springframework.aop.support.Pointcuts 類的靜態(tài)方法來組合,或者使用同一個包中的 ComposablePointcut 類。

    3 .實用切入點實現(xiàn)

    Spring 提供幾個實用的切入點實現(xiàn),一些可以直接使用,另一些需要子類化來實現(xiàn)應(yīng)用相關(guān)的切入點。

    1 )靜態(tài)切入點

    靜態(tài)切入點只基于方法和目標類,而不考慮方法的參數(shù)。靜態(tài)切入點足夠滿足大多數(shù)情況的使用。 Spring 可以只在方法第一次被調(diào)用的時候計算靜態(tài)切入點,不需要在每次方法調(diào)用的時候計算。

    讓我們看一下 Spring 提供的一些靜態(tài)切入點的實現(xiàn)。

    正則表達式切入點

    一個很顯然的指定靜態(tài)切入點的方法是正則表達式。除了 Spring 以外,其他的 AOP 框架也實現(xiàn)了這一點。 org.springframework.aop.support.RegexpMethodPointcut 是一個通用的正則表達式切入點,它使用 Perl 5 的正則表達式的語法。

    使用這個類你可以定義一個模式的列表。如果任何一個匹配,那個切入點將被計算成 true (所以,結(jié)果相當(dāng)于是這些切入點的并集)。

    用法如下。

    <bean id="settersAndAbsquatulatePointcut"?
    ??? class="org.springframework.aop.support.RegexpMethodPointcut">

    ??? <property name="patterns">
    ??????? <list>
    ????????????<value>.*get.*</value>
    ??????????? <value>.*absquatulate</value>
    ??????? </list>
    ??? </property>
    </bean>

    RegexpMethodPointcut 一個實用子類, RegexpMethodPointcutAdvisor 允許我們同時引用一個通知(通知可以是攔截器、 before 通知、 throws 通知等)。這簡化了 bean 的裝配,因為一個 bean 可以同時當(dāng)作切入點和通知,如下所示。

    <bean id="settersAndAbsquatulateAdvisor"?
    ??? class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

    ??? <property name="interceptor">
    ??????? <ref local="beanNameOfAopAllianceInterceptor"/>
    ??? </property>

    ??? <property name="patterns">
    ??????? <list>
    ??????????? <value>.*get.*</value>
    ??????????? <value>.*absquatulate</value>
    ??????? </list>
    ??? </property>

    </bean>

    RegexpMethodPointcutAdvisor 可以用于任何通知類型。 RegexpMethodPointcut 類需要 Jakarta ORO 正則表達式包。

    屬性驅(qū)動的切入點

    一類重要的靜態(tài)切入點是元數(shù)據(jù)驅(qū)動的切入點。它使用元數(shù)據(jù)屬性的值,典型地,使用源代碼級元數(shù)據(jù)。

    2 )動態(tài)切入點

    動態(tài)切入點的演算代價比靜態(tài)切入點高得多。它們不僅考慮靜態(tài)信息,還要考慮方法的參數(shù)。這意味著它們必須在每次方法調(diào)用的時候都被計算,并且不能緩存結(jié)果,因為參數(shù)是變化的。

    控制流切入點

    Spring 的控制流切入點概念上和 AspectJ cflow 切入點一致,雖然沒有其那么強大(當(dāng)前沒有辦法指定一個切入點在另一個切入點后執(zhí)行)。一個控制流切入點匹配當(dāng)前的調(diào)用棧。例如,連接點被 com.mycompany.web 包或者 SomeCaller 類中一個方法調(diào)用的時候,觸發(fā)該切入點。控制流切入點的實現(xiàn)類是 org.springframework.aop.support.ControlFlowPointcut

    4 .切入點超類

    Spring 提供非常實用的切入點的超類幫助你實現(xiàn)你自己的切入點。

    因為靜態(tài)切入點非常實用,你很可能子類化 StaticMethodMatcherPointcut ,如下所示。 這只需要實現(xiàn)一個抽象方法(雖然可以改寫其他的方法來自定義行為)。

    class TestStaticPointcut extends StaticMethodMatcherPointcut {

    ??? public boolean matches(Method m, Class targetClass) {
    ??????? // return true if custom criteria match
    ??? }

    }

    當(dāng)然也有動態(tài)切入點的超類。

    Spring 1.0 RC2 或以上版本,自定義切入點可以用于任何類型的通知。

    5 .自定義切入點

    因為 Spring 中的切入點是 Java 類,而不是語言特性(如 AspectJ ),因此可以定義自定義切入點,無論靜態(tài)還是動態(tài)。但是,沒有直接支持用 AspectJ 語法書寫的復(fù)雜的切入點表達式。不過, Spring 的自定義切入點也可以任意的復(fù)雜。

    現(xiàn)在讓我們看看 Spring AOP 是如何處理通知的。

    1 .通知的生命周期

    Spring 的通知可以跨越多個被通知對象共享,或者每個被通知對象有自己的通知。這分別對應(yīng) per-class per-instance 通知。

    Per-class 通知使用最為廣泛。它適合于通用的通知,如事務(wù) adisor 。它們不依賴被代理的對象的狀態(tài),也不添加新的狀態(tài)。它們僅僅作用于方法和方法的參數(shù)。

    Per-instance 通知適合于導(dǎo)入,來支持混入( mixin )。在這種情況下,通知添加狀態(tài)到被代理的對象。

    可以在同一個 AOP 代理中混合使用共享和 per-instance 通知。

    2 Spring 中通知類型

    Spring 提供幾種現(xiàn)成的通知類型并可擴展提供任意的通知類型。讓我們看看基本概念和標準的通知類型。

    1 Interception around advice

    Spring 中最基本的通知類型是 interception around advice

    Spring 使用方法攔截器的 around 通知是和 AOP 聯(lián)盟接口兼容的。實現(xiàn) around 通知的類需要實現(xiàn)接口 MethodInterceptor

    public interface MethodInterceptor extends Interceptor { ?
    ?? Object invoke(MethodInvocation invocation) throws Throwable;
    }

    invoke() 方法的 MethodInvocation 參數(shù)暴露將被調(diào)用的方法、目標連接點、 AOP 代理和傳遞給被調(diào)用方法的參數(shù)。 invoke() 方法應(yīng)該返回調(diào)用的結(jié)果:連接點的返回值。

    一個簡單的 MethodInterceptor 實現(xiàn)看起來如下。

    public class DebugInterceptor implements MethodInterceptor {

    ??? public Object invoke(MethodInvocation invocation) throws Throwable {
    ??????? System.out.println("Before: invocation=[" + invocation + "]");
    ??????? Object rval = invocation.proceed();
    ??????? System.out.println("Invocation returned");
    ??????? return rval;
    ??? }
    }

    注意 MethodInvocation proceed() 方法的調(diào)用。這個調(diào)用會應(yīng)用到目標連接點的攔截器鏈中的每一個攔截器。大部分攔截器會調(diào)用這個方法,并返回它的返回值。但是,一個 MethodInterceptor ,和任何 around 通知一樣,可以返回不同的值或者拋出一個異常,而不調(diào)用 proceed 方法。但是,沒有好的原因你要這么做。

    MethodInterceptor 提供了和其他 AOP 聯(lián)盟的兼容實現(xiàn)的交互能力。這一節(jié)下面要討論的其他的通知類型實現(xiàn)了 AOP 公共的概念,但是以 Spring 特定的方式。雖然使用特定通知類型有很多優(yōu)點,但如果你需要在其他的 AOP 框架中使用,請堅持使用 MethodInterceptor around 通知類型。注意,目前切入點不能和其他框架交互操作,并且 AOP 聯(lián)盟目前也沒有定義切入點接口。

    2 Before 通知

    Before 通知 是一種簡單的通知類型。 這個通知不需要一個 MethodInvocation 對象,因為它只在進入一個方法前被調(diào)用。

    Before 通知的主要優(yōu)點是它不需要調(diào)用 proceed() 方法,因此沒有無意中忘掉繼續(xù)執(zhí)行攔截器鏈的可能性。

    MethodBeforeAdvice 接口如下所示( Spring API 設(shè)計允許成員變量的 Before 通知,雖然一般的對象都可以應(yīng)用成員變量攔截,但 Spring 有可能永遠不會實現(xiàn)它)。

    public interface MethodBeforeAdvice extends BeforeAdvice {
    ??? void before(Method m, Object[] args, Object target) throws Throwable;
    }

    注意,返回類型是 void Before 通知可以在連接點執(zhí)行之前插入自定義的行為,但是不能改變返回值。如果一個 Before 通知拋出一個異常,這將中斷攔截器鏈的進一步執(zhí)行。這個異常將沿著攔截器鏈后退著向上傳播。如果這個異常是 unchecked 的,或者出現(xiàn)在被調(diào)用的方法的簽名中,它將會被直接傳遞給客戶代碼;否則,它將被 AOP 代理包裝到一個 unchecked 的異常里。

    下面是 Spring 中一個 Before 通知的例子,這個例子計數(shù)所有正常返回的方法。

    public class CountingBeforeAdvice implements MethodBeforeAdvice {

    ??? private int count;
    ??? public void before(Method m, Object[] args, Object target) throws Throwable {
    ??????? ++count;
    ??? }

    ??? public int getCount() {?
    ??????? return count;?
    ??? }

    }

    Before 通知可以被用于任何類型的切入點。

    3 Throws 通知

    如果連接點拋出異常, Throws 通知 在連接點返回后被調(diào)用。 Spring 提供強類型的 Throws 通知。注意,這意味著 org.springframework.aop.ThrowsAdvice 接口不包含任何方法,它是一個標記接口,標識給定的對象實現(xiàn)了一個或多個強類型的 Throws 通知方法。這些方法形式如下。

    afterThrowing([Method], [args], [target], subclassOfThrowable)

    只有最后一個參數(shù)是必需的。這樣從 1 個參數(shù)到 4 個參數(shù),依賴于通知是否對方法和方法的參數(shù)感興趣。下面是 Throws 通知的例子。

    如果拋出 RemoteException 異常(包括子類),這個通知會被調(diào)用。

    public? class RemoteThrowsAdvice implements ThrowsAdvice {

    ??? public void afterThrowing(RemoteException ex) throws Throwable {
    ??????? // Do something with remote exception
    ??? }
    }

    如果拋出 ServletException 異常,下面的通知會被調(diào)用。和上面的通知不一樣,它聲明了 4 個參數(shù),所以它可以訪問被調(diào)用的方法、方法的參數(shù)和目標對象。

    public static class ServletThrowsAdviceWithArguments implements ThrowsAdvice {

    ??? public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
    ??????? // Do something will all arguments
    ?? }
    }

    最后一個例子演示了如何在一個類中使用兩個方法來同時處理 RemoteException ServletException 異常。任意個數(shù)的 throws 方法可以被組合在一個類中。

    public static class CombinedThrowsAdvice implements ThrowsAdvice {

    ??? public void afterThrowing(RemoteException ex) throws Throwable {
    ??????? // Do something with remote exception
    ??? }

    ??? public void afterThrowing(Method m, Object[] args, Object target, ServletException ex){ ?
    ?????? // Do something will all arguments
    ??? }

    }

    Throws 通知可被用于任何類型的切入點。

    4 After Returning 通知

    Spring 中的 After Returning 通知必須實現(xiàn) org.springframework.aop.AfterReturningAdvice 接口,如下所示。

    public interface AfterReturningAdvice extends Advice {
    ??? void afterReturning(Object returnValue, Method m, Object[] args, Object target)? throws Throwable;

    }

    After Returning 通知可以訪問返回值(不能改變)、被調(diào)用的方法、方法的參數(shù)和目標對象。

    下面的 After Returning 通知統(tǒng)計所有成功的沒有拋出異常的方法調(diào)用。

    public class CountingAfterReturningAdvice implements AfterReturningAdvice {

    ??? private int count;
    ??? public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
    ??????? ++count;
    ??? }

    ??? public int getCount() {
    ??????? return count;
    ????}
    }

    這方法不改變執(zhí)行路徑。如果它拋出一個異常,這個異常而不是返回值將被沿著攔截器鏈向上拋出。

    After returning 通知可被用于任何類型的切入點。

    5 Introduction 通知

    Spring Introduction 通知看作一種特殊類型的攔截通知。

    Introduction 需要實現(xiàn) IntroductionAdvisor IntroductionInterceptor 接口。

    public interface IntroductionInterceptor extends MethodInterceptor {
    ??? boolean implementsInterface(Class intf);
    }

    繼承自 AOP 聯(lián)盟 MethodInterceptor 接口的 invoke() 方法必須實現(xiàn)導(dǎo)入,也就是說,如果被調(diào)用的方法是在導(dǎo)入的接口中,導(dǎo)入攔截器負責(zé)處理這個方法調(diào)用,它不能調(diào)用 proceed() 方法。

    Introduction 通知不能被用于任何切入點,因為它只能作用于類層次上,而不是方法。你可以只用 InterceptionIntroductionAdvisor 來實現(xiàn)導(dǎo)入通知,它有下面的方法。

    public interface InterceptionIntroductionAdvisor extends InterceptionAdvisor {

    ??? ClassFilter getClassFilter();
    ??? IntroductionInterceptor getIntroductionInterceptor();
    ??? Class[] getInterfaces();
    }

    這里沒有 MethodMatcher ,因此也沒有和導(dǎo)入通知關(guān)聯(lián)的切入點,只有類過濾是合乎邏輯的。

    getInterfaces() 方法返回 advisor 導(dǎo)入的接口。

    讓我們看看一個來自 Spring 測試套件中的簡單例子。我們假設(shè)想要導(dǎo)入下面的接口到一個或者多個對象中。

    public interface Lockable {
    ??? void lock();
    ??? void unlock();
    ??? boolean locked();
    }

    在這個例子中,我們想要能夠?qū)⒈煌ㄖ獙ο箢愋娃D(zhuǎn)換為 Lockable ,不管它們的類型,并且調(diào)用 lock unlock 方法。如果我們調(diào)用 lock() 方法,我們希望所有 setter 方法拋出 LockedException 異常。這樣我們能添加一個方面使對象 不可變,而它們不需要知道這一點,這是一個很好的 AOP 例子。

    首先,我們需要一個做大量轉(zhuǎn)化的 IntroductionInterceptor 。在這里,我們繼承 org.spring framework.aop.support.DelegatingIntroductionInterceptor 實用類。我們可以直接實現(xiàn) Introduction Interceptor 接口,但是,大多數(shù)情況下, DelegatingIntroductionInterceptor 是最合適的。

    DelegatingIntroductionInterceptor 的設(shè)計是將導(dǎo)入委托到真正實現(xiàn)導(dǎo)入接口的接口,隱藏完成這些工作的攔截器。委托可以使用構(gòu)造方法參數(shù)設(shè)置到任何對象中,默認的委托就是自己(當(dāng)無參數(shù)的構(gòu)造方法被使用時)。這樣,在下面的例子里,委托是 DelegatingIntroduction Interceptor 的子類 LockMixin 。給定一個委托(默認是自身)的 DelegatingIntroductionIntercepto r 實例尋找被這個委托(而不是 IntroductionInterceptor )實現(xiàn)的所有接口,并支持它們中任何一個導(dǎo)入。子類如 LockMixin 也可能調(diào)用 suppressInterflace (Class intf) 方法隱藏不應(yīng)暴露的接口。然而,不管 IntroductionInterceptor 準備支持多少接口, IntroductionAdvisor 將控制哪個接口將被實際暴露。一個導(dǎo)入的接口將隱藏目標的同一個接口的所有實現(xiàn)。

    這樣, LockMixin 繼承 DelegatingIntroductionInterceptor 并自己實現(xiàn) Lockable 。父類自動選擇支持導(dǎo)入的 Lockable ,所以我們不需要指定它。用這種方法我們可以導(dǎo)入任意數(shù)量的接口。

    注意 locked 實例變量的使用,這有效地添加了額外的狀態(tài)到目標對象。

    public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {

    ??? private boolean locked;

    ??? public void lock() {
    ?????? this.locked = true;
    ??? }
    ??? public void unlock() {
    ??????? this.locked = false;
    ??? }

    ??? public boolean locked() {
    ??????? return this.locked;
    ??? }

    ??? public Object invoke(MethodInvocation invocation) throws Throwable {
    ?????? if (locked() && invocation.getMethod().getName().indexOf("set") == 0)
    ??????????? throw new LockedException();

    ??????? return super.invoke(invocation); ???
    ???}
    }

    通常不要需要改寫 invoke() 方法,實現(xiàn) DelegatingIntroductionInterceptor 就足夠了,如果是導(dǎo)入的方法, DelegatingIntroductionInterceptor 實現(xiàn)會調(diào)用委托方法,否則繼續(xù)沿著連接點處理。在現(xiàn)在的情況下,我們需要添加一個檢查:在上鎖狀態(tài)下不能調(diào)用 setter 方法。

    所需的導(dǎo)入 advisor 是很簡單的。只有保存一個獨立的 LockMixin 實例,并指定導(dǎo)入的接口,在這里就是 Lockable 。一個稍微復(fù)雜一點例子可能需要一個導(dǎo)入攔截器(可以定義成 prototype )的引用:在這種情況下, LockMixin 沒有相關(guān)配置,所以我們簡單地使用 new 來創(chuàng)建它。

    public class LockMixinAdvisor extends DefaultIntroductionAdvisor {

    ??? public LockMixinAdvisor() {
    ??????? super(new LockMixin(), Lockable.class);
    ??? }
    }

    我們可以非常簡單地使用這個 advisor ,它不需要任何配置(但是,有一點必要的,就是不可能在沒有 IntroductionAdvisor 的情況下使用 IntroductionInterceptor )。和導(dǎo)入一樣,通常 advisor 必須是針對每個實例的,并且是有狀態(tài)的。我們會有不同的 LockMixinAdvisor 每個被通知對象,會有不同的 LockMixin advisor 組成了被通知對象的狀態(tài)的一部分。

    和其他 advisor 一樣,我們可以使用 Advised.addAdvisor() 方法以編程地方式使用這種 advisor ,或者在 XML 中配置(推薦這種方式)。下面將討論所有代理創(chuàng)建,包括“自動代理創(chuàng)建者”,選擇代理創(chuàng)建以正確地處理導(dǎo)入和有狀態(tài)的混入。


    Spring 中,一個 advisor 就是一個 aspect 的完整的模塊化表示。一般地,一個 advisor 包括通知和切入點。

    撇開導(dǎo)入這種特殊情況,任何 advisor 可被用于任何通知。 org.springframework.aop. support.DefaultPointcutAdvisor 是最通用的 advisor 類。例如,它可以和 MethodInterceptor BeforeAdvice ThrowsAdvice 一起使用。

    Spring 中可以將 advisor 和通知混合在一個 AOP 代理中。例如,你可以在一個代理配置中使用一個對 Around 通知、 Throws 通知和 Before 通知的攔截: Spring 將自動創(chuàng)建必要的攔截器鏈。


    三.用ProxyFactoryBean創(chuàng)建AOP代理


    如果你在為你的業(yè)務(wù)對象使用 Spring IoC 容器(例如, ApplicationContext BeanFactory ),你應(yīng)該會或者你愿意會使用 Spring AOP FactoryBean (記住, factory bean 引入了一個間接層,它能創(chuàng)建不同類型的對象)。

    spring 中創(chuàng)建 AOP proxy 的基本途徑是使用 org.springframework.aop.framework. ProxyFactoryBean 。這樣可以對 pointcut advice 做精確控制。但是,如果你不需要這種控制,那些簡單的選擇可能更適合你。

    1 .基本概要

    ProxyFactoryBean 和其他 Spring FactoryBean 實現(xiàn)一樣,引入一個間接的層次。如果你定義一個名字為 foo ProxyFactoryBean ,引用 foo 的對象所看到的不是 ProxyFactoryBean 實例本身,而是由實現(xiàn) ProxyFactoryBean 的類的 getObject() 方法所創(chuàng)建的對象。這個方法將創(chuàng)建一個包裝了目標對象的 AOP 代理。

    使用 ProxyFactoryBean 或者其他 IoC 可知的類來創(chuàng)建 AOP 代理的最重要的優(yōu)點之一是 IoC 可以管理通知和切入點。這是一個非常的強大的功能,能夠?qū)崿F(xiàn)其他 AOP 框架很難實現(xiàn)的特定的方法。例如,一個通知本身可以引用應(yīng)用對象(除了目標對象,它在任何 AOP 框架中都可以引用應(yīng)用對象),這完全得益于依賴注入所提供的可插入性。

    2 JavaBean 的屬性

    類似于 Spring 提供的絕大部分 FactoryBean 實現(xiàn)一樣, ProxyFactoryBean 也是一個 javabean ,我們可以利用它的屬性來指定你將要代理的目標,指定是否使用 CGLIB

    一些關(guān)鍵屬性來自 org.springframework.aop.framework.ProxyConfig ,它是所有 AOP 代理工廠的父類。這些關(guān)鍵屬性包括:

    ? proxyTargetClass :如果我們應(yīng)該代理目標類,而不是接口,這個屬性的值為 true 。如果這是 true ,我們需要使用 CGLIB

    ? optimize :是否使用強優(yōu)化來創(chuàng)建代理。不要使用這個設(shè)置,除非你了解相關(guān)的 AOP 代理是如何處理優(yōu)化的。目前這只對 CGLIB 代理有效,對 JDK 動態(tài)代理無效(默認)。

    ? frozen :是否禁止通知的改變,一旦代理工廠已經(jīng)配置。默認是 false

    ? exposeProxy :當(dāng)前代理是否要暴露在 ThreadLocal 中,以便它可以被目標對象訪問(它可以通過 MethodInvocation 得到,不需要 ThreadLocal )。如果一個目標需要獲得它的代理,并且 exposeProxy 的值是 ture ,可以使用 AopContext.currentProxy() 方法。

    ? AopProxyFactory :所使用的 AopProxyFactory 具體實現(xiàn)。這個參數(shù)提供了一條途徑來定義是否使用動態(tài)代理、 CGLIB 還是其他代理策略。默認實現(xiàn)將適當(dāng)?shù)剡x擇動態(tài)代理或 CGLIB 。一般不需要使用這個屬性;它的意圖是允許 Spring 1.1 使用另外新的代理類型。

    其他 ProxyFactoryBean 特定的屬性包括:

    ? proxyInterfaces :接口名稱的字符串?dāng)?shù)組。如果這個沒有提供, CGLIB 代理將被用于目標類。

    ? interceptorNames: Advisor interceptor 或其他被應(yīng)用的通知名稱的字符串?dāng)?shù)組。順序是很重要的。這里的名稱是當(dāng)前工廠中 bean 的名稱,包括來自祖先工廠的 bean 的名稱。

    ? Singleton :工廠是否返回一個單獨的對象,無論 getObject() 被調(diào)用多少次。許多 FactoryBean 的實現(xiàn)提供這個方法,默認值是 true 。如果你想要使用有狀態(tài)的通知,(例如,用于有狀態(tài)的 mixin )將這個值設(shè)為 false ,使用 prototype 通知。

    3 .代理接口

    讓我們來看一個簡單的 ProxyFactoryBean 的實際例子。這個例子涉及到一個將被代理的目標 bean ,在這個例子里,這個 bean 被定義為“ personTarget ”;一個 advisor 和一個 interceptor 來提供 advice ;一個 AOP 代理 bean 定義,該 bean 指定目標對象(這里是 personTarget bean ),代理接口和使用的 advice

    <bean id="personTarget" class="com.mycompany.PersonImpl">
    ??? <property name="name"><value>Tony</value></property>
    ??? <property name="age"><value>51</value></property>
    </bean>

    <bean id="myAdvisor" class="com.mycompany.MyAdvisor">
    ??? <property name="someProperty"><value>Custom string property value </value> </property>
    </bean>

    <bean id="debugInterceptor" class="org.springframework.aop.interceptor. NopInterceptor"> </bean>

    <bean id="person"? ??? class="org.springframework.aop.framework.ProxyFactoryBean">
    ??? <property name="proxyInterfaces"><value>com.mycompany.Person</value></property>
    ????<property name="target"><ref local="personTarget"/></property>
    ??? <property name="interceptorNames">
    ??????? <list>
    ??????????? <value>myAdvisor</value>
    ??????????? <value>debugInterceptor</value>
    ??????? </list>
    ??? </property>
    </bean>

    請注意, person bean interceptorNames 屬性提供一個 String 列表,列出的是該 ProxyFactoryBean 使用的,在當(dāng)前 bean 工廠定義的 interceptor 或者 advisor 的名字( advisor interceptor before after returning throws advice 對象皆可)。 Advisor 在該列表中的次序很重要。

    你也許會對該列表為什么不采用 bean 的引用存有疑問。原因就在于如果 ProxyFactory Bean singleton 屬性被設(shè)置為 false ,那么 bean 工廠必須能返回多個獨立的代理實例。如果有任何一個 advisor 本身是 prototype 的,那么它就需要返回獨立的實例,也就是有必要從 bean 工廠獲取 advisor 的不同實例, bean 的引用在這里顯然是不夠的。

    上面定義的“ person bean 定義可以作為 Person 接口的實現(xiàn)來使用,如下所示。

    Person person = (Person) factory.getBean("person");

    在同一個 IoC 的上下文中,其他的 bean 可以依賴于 Person 接口,就像依賴于一個普通的 java 對象一樣。

    <bean id="personUser" class="com.mycompany.PersonUser">
    ??? <property name="person"><ref local="person" /></property>
    </bean>

    在這個例子里, PersonUser 類暴露了一個類型為 Person 的屬性。只要是在用到該屬性的地方, AOP 代理都能透明地替代一個真實的 Person 實現(xiàn)。但是,這個類可能是一個動態(tài)代理類。也就是有可能把它類型轉(zhuǎn)換為一個 Advised 接口(該接口在下面的章節(jié)中論述)。

    4 .代理類

    如果你需要代理的是類,而不是一個或多個接口,又該怎么辦呢 ?

    象一下我們上面的例子,如果沒有 Person 接口,我們需要通知一個叫 Person 的類,而且該類沒有實現(xiàn)任何業(yè)務(wù)接口。在這種情況下,你可以配置 Spring 使用 CGLIB 代理,而不是動態(tài) 代理。你只要在上面的 ProxyFactoryBean 定義中把它的 proxyTargetClass 屬性改成 true 就可以。

    只要你愿意,即使在有接口的情況下,你也可以強迫 Spring 使用 CGLIB 代理。

    CGLIB 代理是通過在運行期產(chǎn)生目標類的子類來進行工作的。 Spring 可以配置這個生成的子類,來代理原始目標類的方法調(diào)用。這個子類是用 Decorator 設(shè)計模式置入到 advice 中的。

    CGLIB 代理對于用戶來說應(yīng)該是透明的。然而,還有以下一些因素需要考慮。

    1 Final 方法不能被通知,因為不能被重寫。

    2 )你需要在你的 classpath 中包括 CGLIB 的二進制代碼,而動態(tài)代理對任何 JDK 都是可用的。

    3 CGLIB 和動態(tài)代理在性能上有微小的區(qū)別,對 Spring 1.0 來說,后者稍快。另外,以后可能會有變化。在這種情況下,性能不是決定性因素。

    posted on 2006-11-01 11:17 OMG 閱讀(1392) 評論(0)  編輯  收藏 所屬分類: Spring

    <2006年11月>
    2930311234
    567891011
    12131415161718
    19202122232425
    262728293012
    3456789

    常用鏈接

    留言簿(1)

    隨筆分類

    隨筆檔案

    IT風(fēng)云人物

    文檔

    朋友

    相冊

    經(jīng)典網(wǎng)站

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 国内成人精品亚洲日本语音| 久久亚洲国产中v天仙www| 亚洲短视频男人的影院| 日本成年免费网站| 一区二区三区免费精品视频| 国产亚洲综合久久| 国产成人综合亚洲一区| 国产AV日韩A∨亚洲AV电影 | 国产免费啪嗒啪嗒视频看看| 暖暖免费高清日本中文| 在线观看永久免费视频网站 | 99久久综合精品免费| 99re视频精品全部免费| **实干一级毛片aa免费| 一区二区视频免费观看| 久久精品免费大片国产大片| 亚洲13又紧又嫩又水多| 亚洲私人无码综合久久网| 亚洲天堂中文资源| 亚洲精品mv在线观看| 欧美在线看片A免费观看| 日韩免费码中文在线观看| 男人天堂2018亚洲男人天堂| 亚洲色大情网站www| 香港特级三A毛片免费观看| 国产精品免费久久久久久久久| 91国内免费在线视频| 91青青青国产在观免费影视| 99精品全国免费观看视频| 国产国产成年年人免费看片| 永久免费的网站在线观看| 51在线视频免费观看视频| 无人影院手机版在线观看免费| 国产成人精品免费视频大全五级| 亚洲国产成人精品女人久久久 | 亚洲一区二区无码偷拍| 免费一区二区无码视频在线播放| 99免费精品视频| av无码国产在线看免费网站| 国产成人精品免费视频大全五级| 亚洲级αV无码毛片久久精品|