Spring中的AOP(三)——基于Annotation的配置方式(一)
為什么80%的碼農都做不了架構師?>>> ??
????AspectJ允許使用注解用于定義切面、切入點和增強處理,而Spring框架則可以識別并根據這些注解來生成AOP代理。Spring只是使用了和AspectJ 5一樣的注解,但并沒有使用AspectJ的編譯器或者織入器,底層依然使用SpringAOP來實現,依然是在運行時動態生成AOP代理,因此不需要增加額外的編譯,也不需要AspectJ的織入器支持。而AspectJ采用編譯時增強,所以AspectJ需要使用自己的編譯器來編譯Java文件,還需要織入器。
????為了啟用Spring對@AspectJ切面配置的支持,并保證Spring容器中的目標Bean被一個或多個切面自動增強,必須在Spring配置文件中配置如下內容(第4、9、10、15行):
????所謂自動增強,指的是Spring會判斷一個或多個切面是否需要對指定的Bean進行增強,并據此自動生成相應的代理,從而使得增強處理在合適的時候被調用。如果不打算使用XML Schema的配置方式,則應該在Spring配置文件中增加如下片段來啟用@AspectJ支持(即上面的<aop:aspectj-autoproxy />和下面創建Bean的方式選擇一種即可啟用@AspectJ支持):
????上面配置的是一個Bean后處理器,該處理器將會為容器中Bean生成AOP代理。
????為了在Spring應用中啟動@AspectJ支持,還需要在用用的類加載路徑下增加兩個AspectJ庫:aspectweaver.jar和aspectjrt.jar,直接使用AspectJ安裝路徑下的lib目錄下的這兩個Jar文件即可,當然,也可以在Spring解壓縮文件夾的lib/aspectj路徑下找到它們。下面是項目內容的截圖:
定義切面Bean
????當啟用了@AspectJ支持后,只要我們在Spring容器中配置一個帶@AspectJ注釋的Bean,Spring將會自動識別該Bean,并將該Bean作為切面處理。下面是一個例子:
????切面類(用@Aspect修飾的類)和其他類一樣可以有方法和屬性的定義,還可能包括切入點、增強處理的定義。當我們使用@Aspect來修飾一個Java類后,Spring將不會把該Bean當成組件Bean處理,因此當Spring容器檢測到某個Bean使用了@AspectJ標注之后,負責自動增強的后處理Bean將會忽略該Bean,不會對該Bean進行任何增強處理。
使用Before增強處理
????當我們在一個切面類里使用@Before來標注一個方法時,該方法將作為Before增強處理。使用@Before標注時,通常需要指定一個value屬性值,該屬性值指定一個切入點表達式(既可以是一個已有的切入點,也可以直接定義切入點表達式),用于指定該增強處理將被織入哪些切入點。看例子:
????上面的程序使用@Aspect修飾了BeforeAdviceTest類,這表明該類是一個切面類,在該貼面里定義了一個permissionCheck方法——這個方法本來沒有什么特殊之處,但因為使用了@Before來標注該方法,這就將該方法轉換成一個Before增強處理。這個@Before注解中,直接指定了切入點表達式,指定com.abc.service包下的類中以before開始的方法的執行作為切入點。現假設我們在com.abc.service下有一個這樣一個類:
????從上面的代碼來看,這個AdviceManager是一個純凈的Java類,它絲毫不知道將被誰來增強,也不知道將被進行怎樣的增強——正式因為AdviceManager類的這種“無知”,才是AOP的最大魅力:目標類可以被無限的增強。
????在Spring配置文件中配置自動搜索Bean組件,配置自動搜索切面類,SpringAOP自動對Bean組件進行增強,下面是Spring配置文件代碼:
????主程序非常簡單,通過Spring容器獲取AdviceManager Bean,并調用Bean的beforeAdvice方法:
package?com.abc.main;import?org.springframework.context.ApplicationContext; import?org.springframework.context.support.ClassPathXmlApplicationContext;import?com.abc.service.AdviceManager;@SuppressWarnings("resource") public?class?AOPTest?{public?static?void?main(String[]?args)?{ApplicationContext?context?=?new?ClassPathXmlApplicationContext("applicationContext.xml");AdviceManager?manager?=?context.getBean(AdviceManager.class);manager.beforeAdvice();} }????執行主程序,將看到以下結果:
????使用Before增強處理只能在目標方法執行之前織入增強,使用Before增強處理無需理會目標方法的執行,所以Before處理無法阻止目標方法的執行。Before增強處理執行時,目標方法還未獲得執行機會,所以Before增強處理無法訪問目標方法的返回值。
使用AfterReturning增強處理
????和使用@Before注解的使用類似,使用@AfterReturning來標注一個AfterReturning增強處理,該處理將在目標方法正常完成后被織入。使用@AfterReturning時可以指定兩個屬性:
pointcut/value:這兩個屬性的作用是一樣的,都用于指定該切入點對應的切入表達式。同樣的,既可以是一個已有的切入點,也可以是直接定義的切入點。當指定了pointcut屬性后,value的屬性值將會被覆蓋
returning:指定一個返回值形參名,增強處理定義的方法可以通過該形參名來訪問目標方法的返回值。
? ? 在com.abc.advice包下面增加AfterReturningAdviceTest,這個類定義了一個AfterReturning增強處理:
? ? 并在AdviceManager類中增加以下內容:
//將被AfterReturningAdviceTest的log方法匹配 public?String?afterReturning()?{System.out.println("方法:afterReturning");return?"afterReturning方法"; }? ? 正如上面程序中看到的,程序中使用@AfterReturning注解時,指定了一個returning屬性,該屬性的返回值是returnValue,這表明允許在增強方法log中使用名為returnValue的形參,該形參代表目標方法的返回值。在測試類AOPTest的main方法中增加調用本方法的語句,運行測試類,可以看到以下結果:
????@AfterReturning注解的returning屬性所指定的形參名必須對應增強處理中的一個形參名,當目標方法執行以后,返回值作為相應的參數傳入給增強處理方法。
????需要注意的是,使用@AfterReturning屬性還有一個額外的作用,它可用于限定切入點之匹配具有對應返回值類型的方法——假設上面的log方法的參數returnValue的類型為String,那么該切入點只匹配com.abc.service.impl包下的返回值為String的所有方法。當然,上面的log方法返回值類型為Object,表明該切入點可匹配任何返回值的方法。除此之外,雖然AfterReturning增強處理可以訪問到目標方法的返回值,但它不可改變這個返回值。
使用AfterThrowing增強處理
????使用@AfterThrowing注解可用于標注一個AfterThrowing增強處理,這個處理主要用于處理陳旭中未處理的異常。使用這個注解時可以指定兩個屬性:
pointcut/value:這兩個屬性的作用是一樣的,都用于指定該切入點對應的切入表達式。同樣的,既可以是一個已有的切入點,也可以是直接定義的切入點。當指定了pointcut屬性后,value的屬性值將會被覆蓋
throwing:指定一個返回值形參名,增強處理定義的方法可通過該形參名來訪問目標方法中所拋出的異常對象。
?????在com.abc.advice包下面增加AfterThrowingAdviceTest,這個類定義了一個AfterThrowing增強處理:
????并在AdviceManager類中增加以下內容:
//將被AfterThrowingAdviceTest的handleException方法匹配 public?void?afterThrowing()?{System.out.println("方法:?afterThrowing");try?{int?a?=?10?/?0;}?catch?(ArithmeticException?ae)?{System.out.println("算術異常已被處理");}String?s?=?null;System.out.println(s.substring(0,3)); }????正如上面程序中看到的,程序中使用@AfterThrowing注解時,指定了一個throwing屬性,該屬性的值是ex,這表明允許在增強方法log中使用名為ex的形參,該形參代表目標方法的拋出的異常對象。運行測試類,可以看到以下結果:
????需要注意的是:如果一個異常在程序內部已經處理,那么Spring AOP將不會處理該異常。只有當目標方法拋出一個未處理的異常時,該異常將會作為對應的形參傳給增強處理的方法。和AfterReturning類似的是,正確方法的參數類型可以限定切點只匹配指定類型的異常——假如上面的handleException方法的參數類型為NullPointerException,那么如果目標方法只拋出了ArithmaticException,則Spring AOP將不會處理這個異常。當然,handleException的參數類型為Throwable,則匹配了所有的Exception。
????從測試結果中可以看到,AfterThrowing處理雖然可以對目標方法的異常進行處理,但這種處理與直接使用catch捕捉不同:catch捕捉意味著完全處理該異常,如果catch塊中沒有重新拋出新異常,則該方法可以正常結束;而AfterThrowing處理雖然處理了該異常,但它不能完全處理該異常,這個異常依然會傳播到上一級調用者(本例中為JVM,故會導致程序終止)。
????《Spring中的AOP系列三、四、五》的代碼在這里:點擊下載,歡迎留言提意見。
????【未完,待續】
轉載于:https://my.oschina.net/itblog/blog/210718
總結
以上是生活随笔為你收集整理的Spring中的AOP(三)——基于Annotation的配置方式(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 扩展方法
- 下一篇: Spring MVC和Struts2