Java高級框架應(yīng)用開發(fā)案例教程(十一).pptx_第1頁
Java高級框架應(yīng)用開發(fā)案例教程(十一).pptx_第2頁
Java高級框架應(yīng)用開發(fā)案例教程(十一).pptx_第3頁
Java高級框架應(yīng)用開發(fā)案例教程(十一).pptx_第4頁
Java高級框架應(yīng)用開發(fā)案例教程(十一).pptx_第5頁
已閱讀5頁,還剩50頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

Java高級框架應(yīng)用開發(fā)案例教程(十一) Spring面向方面編程 和事務(wù)處理,主要內(nèi)容,11.1 AOP概念 11.2 Spring的切入點 11.3 Spring的通知類型 11.4 Spring中的advisor 11.5 用ProxyFactoryBean創(chuàng)建AOP代理 11.6 事務(wù)處理 11.7 項目案例,11.1 AOP概念,我們來介紹另外一個重要的概念:AOP(Aspect Oriented Programming), 也就是面向方面編程的技術(shù)。AOP是基于IoC基礎(chǔ)上,是對OOP的有益補(bǔ)充。 AOP將應(yīng)用系統(tǒng)分為兩部分,核心業(yè)務(wù)邏輯Core business concerns及橫向的通用邏輯,也就是所謂的方面Crosscutting enterprise concerns,例如所有大中型應(yīng)用都要涉及到的持久化管理(Persistent),事務(wù)管理(Transaction Management),安全管理(Security),日志管理(Logging),和調(diào)試管理(Debugging)等等。 11.1 AOP概念 讓我們從定義一些重要的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都是以攔截器做通知模型,維護(hù)一個“圍繞”連接點的攔截器鏈。 切入點(Pointcut): 指定一個通知將被引發(fā)的一系列連接點的集合。AOP框架必須允許開發(fā)者指定切入點:例如,使用正則表達(dá)式。 引入(Introduction): 添加方法或字段到被通知的類。Spring允許引入新的接口到任何被通知的對象。例如,你可以使用一個引入使任何對象實現(xiàn) IsModified接口,來簡化緩存。 目標(biāo)對象(Target Object): 包含連接點的對象。也被稱作被通知或被代理對象。,11.1 AOP概念,AOP代理(AOP Proxy): AOP框架創(chuàng)建的對象,包含通知。在Spring中,AOP代理可以是JDK動態(tài)代理或者CGLIB代理。 編織(Weaving): 組裝方面來創(chuàng)建一個被通知對象。這可以在編譯時完成(例如使用AspectJ編譯器),也可以在運行時完成。Spring和其他純Java AOP框架一樣,在運行時完成織入。 各種通知類型包括: Around通知: 包圍一個連接點的通知,如方法調(diào)用。這是最強(qiáng)大的通知。Aroud通知在方法調(diào)用前后完成自定義的行為。它們負(fù)責(zé)選擇繼續(xù)執(zhí)行連接點或通過返回它們自己的返回值或拋出異常來短路執(zhí)行。 Before通知: 在一個連接點之前執(zhí)行的通知,但這個通知不能阻止連接點前的執(zhí)行(除非它拋出一個異常)。 Throws通知: 在方法拋出異常時執(zhí)行的通知。Spring提供強(qiáng)制類型的Throws通知,因此你可以書寫代碼捕獲感興趣的異常(和它的子類),不需要從Throwable或Exception強(qiáng)制類型轉(zhuǎn)換。 After returning通知: 在連接點正常完成后執(zhí)行的通知,例如,一個方法正常返回,沒有拋出異常。,11.1 AOP概念,其中Around通知是最通用的通知類型。大部分基于攔截的AOP框架,如Nanning和JBoss4,只提供 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的 層次選定目標(biāo)。例如,提供聲明式事務(wù)管理的around通知可以被應(yīng)用到跨越多個對象的一組方法上。 因此切入點構(gòu)成了AOP的結(jié)構(gòu)要素。,11.1 AOP概念,下面讓我們實現(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方法共用三個參數(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對象的引用。,11.1 AOP概念,11.1 AOP概念,在下面的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也鼓勵這樣做,11.1 AOP概念,11.1 AOP概念,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(); ,11.1 AOP概念,我們從讀入和處理配置文件開始,接下來馬上要創(chuàng)建它。這個配置文件將作為粘合程序不同部分的“膠水”。讀入和處理配置文件后,我們會得到一個創(chuàng)建工廠ctx。任何一個Spring管理的對象都必須通過這個工廠來創(chuàng)建。對象通過工廠創(chuàng)建后便可正常使用。,11.1 AOP概念,僅僅用配置文件便可把程序的每一部分組裝起來。 代碼: com.ascenttech.springaop.test.Bean theAdvisor ,11.1 AOP概念, com.ascenttech.springaop.test.Bean.theMethod ,11.1 AOP概念,四個bean定義的次序并不重要。我們現(xiàn)在有了一個advice,一個包含了正則表達(dá)式pointcut的advisor,一個主程序類和一個配置好的接口,通過工廠ctx,這個接口返回自己本身實現(xiàn)的一個引用。 BeanImpl和TestBeforeAdvice都是直接配置。我們用一個唯一的ID創(chuàng)建一個bean元素,并指定了一個實現(xiàn)類。這就是全部的工作。 advisor通過Spring framework提供的一個RegexMethodPointcutAdvisor類來實現(xiàn)。我們用advisor的一個屬性來指定它所需的advice-bean。第二個屬性則用正則表達(dá)式定義了pointcut,確保良好的性能和易讀性。 最后配置的是bean,它可以通過一個工廠來創(chuàng)建。bean的定義看起來比實際上要復(fù)雜。bean是ProxyFactoryBean的一個實現(xiàn),它是Spring framework的一部分。這個bean的行為通過一下的三個屬性來定義: 屬性proxyInterface定義了接口類。 屬性target指向本地配置的一個bean,這個bean返回一個接口的實現(xiàn)。 屬性interceptorNames是唯一允許定義一個值列表的屬性。這個列表包含所有需要在beanTarget上執(zhí)行的advisor。注意,advisor列表的次序是非常重要的。,11.2 Spring的切入點,11.2 Spring的切入點 讓我們看看Spring如何處理切入點這個重要的概念。 1.概念 Spring的切入點模型能夠使切入點獨立于通知類型被重用。 同樣的切入點有可能接受不同的通知。 org.springframework.aop.Pointcut 接口是重要的接口,用來指定通知到特定的類和方法目標(biāo)。完整的接口定義如下: public interface Pointcut ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); 將Pointcut接口分成兩個部分有利于重用類和方法的匹配部分,并且組合細(xì)粒度的操作(如和另一個方法匹配器執(zhí)行一個”并“的操作)。,11.2 Spring的切入點,ClassFilter接口被用來將切入點限制到一個給定的目標(biāo)類的集合。 如果matches()永遠(yuǎn)返回true,所有的目標(biāo)類都將被匹配。 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) 方法被用來測試這個切入點是否匹配目標(biāo)類的給定方法。這個測試可以在AOP代理創(chuàng)建的時候執(zhí)行,避免在所有方法調(diào)用時都需要進(jìn)行測試。如果2個參數(shù)的匹配方法對某個方法返回true,并且MethodMatcher的 isRuntime()也返回true,那么3個參數(shù)的匹配方法將在每次方法調(diào)用的時候被調(diào)用。這使切入點能夠在目標(biāo)通知被執(zhí)行之前立即查看傳遞給方法調(diào)用的參數(shù)。 大部分MethodMatcher都是靜態(tài)的,意味著isRuntime()方法返回false。這種情況下3個參數(shù)的匹配方法永遠(yuǎn)不會被調(diào)用。 如果可能,盡量使切入點是靜態(tài)的,使當(dāng)AOP代理被創(chuàng)建時,AOP框架能夠緩存切入點的 測試結(jié)果。,11.2 Spring的切入點,2.切入點的運算 Spring支持的切入點的運算有: 值得注意的是并和交。 并表示只要任何一個切入點匹配的方法。 交表示兩個切入點都要匹配的方法。 并通常比較有用。 切入點可以用org.springframework.aop.support.Pointcuts 類的靜態(tài)方法來組合,或者使用同一個包中的ComposablePointcut類。 3.實用切入點實現(xiàn) Spring提供幾個實用的切入點實現(xiàn)。一些可以直接使用。另一些需要子類化來實現(xiàn)應(yīng)用相 關(guān)的切入點。 3.1.靜態(tài)切入點 靜態(tài)切入點只基于方法和目標(biāo)類,而不考慮方法的參數(shù)。靜態(tài)切入點足夠滿足大多數(shù)情況 的使用。Spring可以只在方法第一次被調(diào)用的時候計算靜態(tài)切入點,不需要在每次方法調(diào)用的時候計算。,11.2 Spring的切入點,讓我們看一下Spring提供的一些靜態(tài)切入點的實現(xiàn)。 1).正則表達(dá)式切入點 一個很顯然的指定靜態(tài)切入點的方法是正則表達(dá)式。除了Spring以外,其它的AOP框架也實現(xiàn)了這一點。org.springframework.aop.support.RegexpMethodPointcut是一個通用的正則表達(dá)式切入點,它使用Perl 5的正則表達(dá)式的語法。 使用這個類你可以定義一個模式的列表。如果任何一個匹配,那個切入點將被計算成 true。(所以結(jié)果相當(dāng)于是這些切入點的并集)。 用法如下: .*get.* .*absquatulate ,11.2 Spring的切入點,RegexpMethodPointcut一個實用子類, RegexpMethodPointcutAdvisor,允許我們同時引用一個通知。 (記住通知可以是攔截器,before通知,throws通知等等。)這簡化了bean的裝配,因為一個bean 可以同時當(dāng)作切入點和通知,如下所示: .*get.* .*absquatulate RegexpMethodPointcutAdvisor可以用于任何通知類型。 RegexpMethodPointcut類需要Jakarta ORO正則表達(dá)式包。 2)屬性驅(qū)動的切入點 一類重要的靜態(tài)切入點是元數(shù)據(jù)驅(qū)動的 切入點。 它使用元數(shù)據(jù)屬性的值:典型地,使用源代碼級元數(shù)據(jù)。,11.2 Spring的切入點,3.2.動態(tài)切入點 動態(tài)切入點的演算代價比靜態(tài)切入點高的多。它們不僅考慮靜態(tài)信息,還要考慮方法的 參數(shù)。這意味著它們必須在每次方法調(diào)用的時候都被計算;并且不能緩存結(jié)果 ,因為參數(shù)是變化的。 1).控制流切入點 Spring的控制流切入點概念上和AspectJ的cflow 切入點一致,雖然沒有其那么強(qiáng)大(當(dāng)前沒有辦法指定一個切入點在另一個切入點后執(zhí)行)。 一個控制流切入點匹配當(dāng)前的調(diào)用棧。例如,連接點被 com.mycompany.web包或者 SomeCaller類中一個方法調(diào)用的時候,觸發(fā)該切入點??刂屏髑腥朦c的實現(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或以上版本,自定義切入點可以用于任何類型的通知。,11.2 Spring的切入點,5.自定義切入點 因為Spring中的切入點是Java類,而不是語言特性(如AspectJ),因此可以定義自定義切入點,無論靜態(tài)還是動態(tài)。但是,沒有直接支持用AspectJ語法書寫的復(fù)雜的切入點表達(dá)式。不過, Spring的自定義切入點也可以任意的復(fù)雜。,11.3 Spring的通知類型,11.3.Spring的通知類型 現(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通知。,11.3 Spring的通知類型,2.Spring中通知類型 Spring提供幾種現(xiàn)成的通知類型并可擴(kuò)展提供任意的通知類型。讓我們看看基本概念和 標(biāo)準(zhǔ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)用的方法、目標(biāo)連接點、AOP代理和傳遞給被調(diào)用方法的參數(shù)。 invoke()方法應(yīng)該返回調(diào)用的結(jié)果:連接點的返回值。,11.3 Spring的通知類型,一個簡單的MethodInterceptor實現(xiàn)看起來如下: public class DebugInterceptor implements MethodInterceptor public Object invoke(MethodInvocation invocation) throws Throwable System.out.println(“Before: invocation=“ + invocation + “); Object rval = ceed(); System.out.println(“Invocation returned“); return rval; 注意MethodInvocation的proceed()方法的調(diào)用。 這個調(diào)用會應(yīng)用到目標(biāo)連接點的攔截器鏈中的每一個攔截器。大部分?jǐn)r截器會調(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)盟目前也沒有定義切入 點接口。,11.3 Spring的通知類型,2).Before通知 Before通知是一種簡單的通知類型。 這個通知不需要一個MethodInvocation對象,因為它只在進(jìn)入一個方法 前被調(diào)用。 Before通知的主要優(yōu)點是它不需要調(diào)用proceed() 方法, 因此沒有無意中忘掉繼續(xù)執(zhí)行攔截器鏈的可能性。 MethodBeforeAdvice接口如下所示。 (Spring的API設(shè)計允許成員變量的before通知,雖然一般的對象都可以應(yīng)用成員變量攔截,但Spring 有可能永遠(yuǎn)不會實現(xiàn)它)。 public interface MethodBeforeAdvice extends BeforeAdvice void before(Method m, Object args, Object target) throws Throwable; 注意返回類型是void。 Before通知可以在連接點執(zhí)行之前 插入自定義的行為,但是不能改變返回值。如果一個before通知拋出一個異常,這將中斷攔截器 鏈的進(jìn)一步執(zhí)行。這個異常將沿著攔截器鏈后退著向上傳播。如果這個異常是unchecked的,或者出現(xiàn)在被調(diào)用的方法的簽名中,它將會被直接傳遞給客戶代碼;否則,它將被AOP代理包裝到一個unchecked 的異常里。,11.3 Spring的通知類型,下面是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通知可以被用于任何類型的切入點。,11.3 Spring的通知類型,3)Throws通知 如果連接點拋出異常,Throws通知在連接點返回后被調(diào)用。Spring提供強(qiáng)類型的throws通知。注意這意味著 org.springframework.aop.ThrowsAdvice接口不包含任何方法:它是一個標(biāo)記接口,標(biāo)識給定的對象實現(xiàn)了一個或多個強(qiáng)類型的throws通知方法。這些方法形式 如下: afterThrowing(Method, args, target, subclassOfThrowable) 只有最后一個參數(shù)是必需的。 這樣從一個參數(shù)到四個參數(shù),依賴于通知是否對方法和方法 的參數(shù)感興趣。下面是throws通知的例子。 如果拋出RemoteException異常(包括子類), 這個通知會被調(diào)用 public class RemoteThrowsAdvice implements ThrowsAdvice public void afterThrowing(RemoteException ex) throws Throwable / Do something with remote exception ,11.3 Spring的通知類型,如果拋出ServletException異常, 下面的通知會被調(diào)用。和上面的通知不一樣,它聲明了四個參數(shù),所以它可以訪問被調(diào)用的方法,方法的參數(shù)和目標(biāo)對象: 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通知可被用于任何類型的切入點。,11.3 Spring的通知類型,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ù)和 目標(biāo)對象。,11.3 Spring的通知類型,下面的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通知可被用于任何類型的切入點。,11.3 Spring的通知類型,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)入攔截器負(fù)責(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)入的接口。,11.3 Spring的通知類型,讓我們看看一個來自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.springframework.aop.support.DelegatingIntroductionInterceptor 實用類。我們可以直接實現(xiàn)IntroductionInterceptor接口,但是大多數(shù)情況下 DelegatingIntroductionInterceptor是最合適的。,11.3 Spring的通知類型,DelegatingIntroductionInterceptor的設(shè)計是將導(dǎo)入 委托到真正實現(xiàn)導(dǎo)入接口的接口,隱藏完成這些工作的攔截器。委托可以使用構(gòu)造方法參數(shù) 設(shè)置到任何對象中;默認(rèn)的委托就是自己(當(dāng)無參數(shù)的構(gòu)造方法被使用時)。這樣在下面的例子里,委托是DelegatingIntroductionInterceptor的子類 LockMixin。給定一個委托(默認(rèn)是自身)的 DelegatingIntroductionInterceptor實例尋找被這個委托(而不 是IntroductionInterceptor)實現(xiàn)的所有接口,并支持它們中任何一個導(dǎo)入。子類如 LockMixin也可能調(diào)用suppressInterflace(Class intf) 方法隱藏不應(yīng)暴露的接口。然而,不管IntroductionInterceptor 準(zhǔn)備支持多少接口,IntroductionAdvisor將控制哪個接口將被實際 暴露。一個導(dǎo)入的接口將隱藏目標(biāo)的同一個接口的所有實現(xiàn)。,11.3 Spring的通知類型,LockMixin繼承DelegatingIntroductionInterceptor 并自己實現(xiàn)Lockable。父類自動選擇支持導(dǎo)入的Lockable,所以我們不需要指定它。 用這種方法我們可以導(dǎo)入任意數(shù)量的接口。 注意locked實例變量的使用。這有效地添加額外的狀態(tài)到目標(biāo) 對象。 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() ,11.3 Spring的通知類型,通常不要需要改寫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)的混入。,11.4 Spring中的advisor,11.4 Spring中的advisor 在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)建必要的攔截器鏈。,11.5 用ProxyFactoryBean創(chuàng)建AOP代理,11.5用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)建一個包裝了目標(biāo)對象 的AOP代理。 使用ProxyFactoryBean或者其他IoC可知的類來創(chuàng)建AOP代理的最重要的優(yōu)點之一是IoC可以管理通知和切入點。這是一個非常的強(qiáng)大的功能,能夠?qū)崿F(xiàn)其他AOP框架很難實現(xiàn)的特定的方法。例如,一個通知本身可以引用應(yīng)用對象(除了目標(biāo)對象, 它在任何AOP框架中都可以引用應(yīng)用對象),這完全得益于依賴注入所提供的可插入性。,11.5 用ProxyFactoryBean創(chuàng)建AOP代理,2.JavaBean的屬性 類似于Spring提供的絕大部分FactoryBean實現(xiàn)一樣, ProxyFactoryBean也是一個javabean,我們可以利用它的屬性來: 指定你將要代理的目標(biāo) 指定是否使用CGLIB 一些關(guān)鍵屬性來自org.springframework.aop.framework.ProxyConfig :它是所有AOP代理工廠的父類。這些關(guān)鍵屬性包括: proxyTargetClass: 如果我們應(yīng)該代理目標(biāo)類, 而不是接口,這個屬性的值為true。如果這是true,我們需要使用CGLIB。 optimize: 是否使用強(qiáng)優(yōu)化來創(chuàng)建代理。不要使用 這個設(shè)置,除非你了解相關(guān)的AOP代理是如何處理優(yōu)化的。目前這只對CGLIB代理有效;對JDK 動態(tài)代理無效(默認(rèn))。 frozen: 是否禁止通知的改變,一旦代理工廠已經(jīng)配置。 默認(rèn)是false。 exposeProxy: 當(dāng)前代理是否要暴露在ThreadLocal中, 以便它可以被目標(biāo)對象訪問。(它可以通過MethodInvocation得到,不需要ThreadLocal)。 如果一個目標(biāo)需要獲得它的代理并且exposeProxy的值是ture,可以使用 AopContext.currentProxy()方法。 aopProxyFactory: 所使用的AopProxyFactory具體實現(xiàn)。 這個參數(shù)提供了一條途徑來定義是否使用動態(tài)代理、CGLIB還是其他代理策略。默認(rèn)實現(xiàn)將適當(dāng)?shù)剡x擇動態(tài) 代理或CGLIB。一般不需要使用這個屬性;它的意圖是允許Spring 1.1使用另外新的代理類型。,11.5 用ProxyFactoryBean創(chuàng)建AOP代理,其他ProxyFactoryBean特定的屬性包括: proxyInterfaces: 接口名稱的字符串?dāng)?shù)組。如果這個 沒有提供,CGLIB代理將被用于目標(biāo)類。 interceptorNames: Advisor、interceptor或其他 被應(yīng)用的通知名稱的字符串?dāng)?shù)組。順序是很重要的。這里的名稱是當(dāng)前工廠中bean的名稱,包 括來自祖先工廠的bean的名稱。 singleton: 工廠是否返回一個單獨的對象,無論 getObject()被調(diào)用多少次。許多FactoryBean 的實現(xiàn)提供這個方法。默認(rèn)值是true。如果你想要使用有狀態(tài)的通知例如,用于有狀態(tài)的 mixin將這個值設(shè)為false,使用prototype通知。 3代理接口 讓我們來看一個簡單的ProxyFactoryBean的實際例子。這個例子涉及到 : 一個將被代理的目標(biāo)bean,在這個例子里,這個bean的被定義為“personTarget“. 一個advisor和一個interceptor來提供advice. 一個AOP代理bean定義,該bean指定目標(biāo)對象(這里是personTarget bean), 代理接口,和使用的advice.,11.5 用ProxyFactoryBean創(chuàng)建AOP代理, Tony 51 Custom string property value com.mycompany.Person myAdvisor debugInterceptor ,11.5 用ProxyFactoryBean創(chuàng)建AOP代理,請注意:person bean的interceptorNames屬性提供一個String列表, 列出的是該ProxyFactoryBean使用的,在當(dāng)前bean工廠定義的interceptor或者advisor的 名字(advisor,interceptor,before,after returning,和throws advice 對象皆可)。 Advisor在該列表中的次序很重要。 你也許會對該列表為什么不采用bean的引用存有疑問。 原因就在于如果ProxyFactoryBean的singleton屬性被設(shè)置為false, 那么bean工廠必須能返回多個獨立的代理實例。 如果有任何一個advisor本身是prototype的,那么它就需要返回獨立的實例, 也就是有必要從bean工廠獲取advisor的不同實例,bean的引用在這里顯然是不夠的。 上面定義的“person”bean定義可以作為Person接口的實現(xiàn)來使用,如下所示: Person person = (Person) factory.getBean(“person“); 在同一個IoC的上下文中,其他的bean可以依賴于Person接口,就象依賴于一個普通的java對象一樣。 ,11.5 用ProxyFactoryBean創(chuàng)建AOP代理,在這個例子里,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就行了。 只要你愿意,即使在有接口的情況下,你也可以強(qiáng)迫Spring使用CGLIB代理。 CGLIB代理是通過在運行期產(chǎn)生目標(biāo)類的子類來進(jìn)行工作的。 Spring可以配置這個生成的子類,來代理原始目標(biāo)類的方法調(diào)用。這個子類是用 Decorator設(shè)計模式置入到advice中的。 CGLIB代理對于

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論