轉(zhuǎn)自: http://blog.csdn.net/a906998248/article/details/7514969
?
一、什么是 AOP。
AOP(Aspect Orient Programming),也就是面向切面編程 ??梢赃@樣理解, 面向?qū)ο缶幊蹋∣OP)是從靜態(tài)角度考慮程序結(jié)構(gòu),面向切面編程(AOP)是從動態(tài)角度考慮程序運(yùn)行過程 。
?
二、AOP 的作用。
常常通過 AOP 來處理一些 具有橫切性質(zhì)的系統(tǒng)性服務(wù) ,如 事物管理、安全檢查、緩存、對象池管理 等,AOP 已經(jīng)成為一種非常常用的解決方案。
?
三、AOP 的實現(xiàn)原理。
如圖: AOP 實際上是由目標(biāo)類的代理類實現(xiàn)的 。 AOP 代理其實是由 AOP 框架動態(tài)生成的一個對象,該對象可作為目標(biāo)對象使用 。A OP 代理包含了目標(biāo)對象的全部方法,但 AOP 代理中的方法與目標(biāo)對象的方法存在差異,AOP 方法在特定切入點添加了增強(qiáng)處理,并回調(diào)了目標(biāo)對象的方法 。
?
四、Spring 中對 AOP 的支持
Spring 中 AOP 代理由 Spring 的 IoC 容器負(fù)責(zé)生成、管理,其依賴關(guān)系也由 IoC 容器負(fù)責(zé)管理 。因此,AOP 代理可以直接使用容器中的其他 Bean 實例作為目標(biāo),這種關(guān)系可由 IoC 容器的依賴注入提供。 Spring 默認(rèn)使用 Java 動態(tài)代理來創(chuàng)建 AOP 代理 , 這樣就可以為任何接口實例創(chuàng)建代理了。當(dāng)需要代理的類不是代理接口的時候, Spring 自動會切換為使用 CGLIB 代理,也可強(qiáng)制使用 CGLIB 。?
AOP 編程其實是很簡單的事情。縱觀 AOP 編程, 其中需要程序員參與的只有三個部分:
?
- 定義普通業(yè)務(wù)組件。
- 定義切入點,一個切入點可能橫切多個業(yè)務(wù)組件。
- 定義增強(qiáng)處理,增強(qiáng)處理就是在 AOP 框架為普通業(yè)務(wù)組件織入的處理動作。
?
所以進(jìn)行 AOP 編程的關(guān)鍵就是定義切入點和定義增強(qiáng)處理。一旦定義了合適的切入點和增強(qiáng)處理,AOP 框架將會自動生成 AOP 代理,即: 代理對象的方法 = 增強(qiáng)處理 + 被代理對象的方法 。
?
五、Spring 中 AOP 的實現(xiàn)。
Spring 有如下兩種選擇來定義切入點和增強(qiáng)處理。
?
- 基于 Annotation 的“零配置”方式:使用@Aspect、@Pointcut等 Annotation 來標(biāo)注切入點和增強(qiáng)處理。
- 基于 XML 配置文件的管理方式:使用 Spring 配置文件來定義切入點和增強(qiáng)點。
?
1、基于 Annotation 的“零配置”方式。
(1)、首先啟用 Spring 對 @AspectJ 切面配置的支持。
?
- <?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:aop= "http://www.springframework.org/schema/aop" ????????
- ???????xsi:schemaLocation="http: //www.springframework.org/schema/beans ??
- ???????????http: //www.springframework.org/schema/beans/spring-beans-3.0.xsd ??
- ???????????http: //www.springframework.org/schema/aop ??
- ???????http: //www.springframework.org/schema/beans/spring-aop-3.0.xsd"> ??
- ????????<!--?啟動對 @AspectJ 注解的支持?-->??
- ????????<aop:aspectj-autoproxy/>??
- </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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/beans/spring-aop-3.0.xsd"> <!-- 啟動對@AspectJ注解的支持 --> <aop:aspectj-autoproxy/> </beans>
如果不打算使用 Spring 的 XML Schema 配置方式,則應(yīng)該在 Spring 配置文件中增加如下片段來啟用@AspectJ 支持。
?
?
- <!--?啟用 @AspectJ ?支持?-->??
- <bean? class = "org.springframeword.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" ?/>??
<!-- 啟用@AspectJ 支持 --> <bean class="org.springframeword.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
?
?
(2)、定義切面 Bean。
當(dāng)啟動了@AspectJ 支持后,只要在 Spring 容器中配置一個帶@Aspect 注釋的 Bean, Spring 將會自動識別該 Bean 并作為切面處理。
?
- //?使用@Aspect?定義一個切面類 ??
- @Aspect ??
- public ? class ?LogAspect?{??
- ???????? //?定義該類的其他內(nèi)容 ??
- ????????...??
- }??
// 使用@Aspect 定義一個切面類 @Aspect public class LogAspect { // 定義該類的其他內(nèi)容 ... }
?
?
(3)、定義 Before 增強(qiáng)處理。
?
?
- //?定義一個切面 ??
- @Aspect ??
- public ? class ?BeforeAdviceTest?{??
- ???? //?匹配?com.wicresoft.app.service.impl?包下所有類的所有方法作為切入點 ??
- ???? @Before ( "execution(*?com.wicresoft.app.service.impl.*.*(..))" )??
- ???? public ? void ?authorith(){??
- ????????System.out.println( "模擬進(jìn)行權(quán)限檢查。" );??
- ????}??
- }??
// 定義一個切面 @Aspect public class BeforeAdviceTest { // 匹配 com.wicresoft.app.service.impl 包下所有類的所有方法作為切入點 @Before("execution(* com.wicresoft.app.service.impl.*.*(..))") public void authorith(){ System.out.println("模擬進(jìn)行權(quán)限檢查。"); } }
?
?
上面使用@Before Annotation 時,直接指定了切入點表達(dá)式,指定匹配?
com.wicresoft.app.service.impl
包下所有類的所有方法執(zhí)行作為切入點。
關(guān)于這個表達(dá)式的規(guī)則如下圖。
?
(4)、定義 AfterReturning 增強(qiáng)處理。
?
- //?定義一個切面 ??
- @Aspect ??
- public ? class ?AfterReturningAdviceTest?{??
- ???? //?匹配?com.wicresoft.app.service.impl?包下所有類的所有方法作為切入點 ??
- ???? @AfterReturning (returning= "rvt" ,?pointcut= "execution(*?com.wicresoft.app.service.impl.*.*(..))" )??
- ???? public ? void ?log(Object?rvt)?{??
- ????????System.out.println( "模擬目標(biāo)方法返回值:" ?+?rvt);??
- ????????System.out.println( "模擬記錄日志功能..." );??
- ????}??
- }??
// 定義一個切面 @Aspect public class AfterReturningAdviceTest { // 匹配 com.wicresoft.app.service.impl 包下所有類的所有方法作為切入點 @AfterReturning(returning="rvt", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))") public void log(Object rvt) { System.out.println("模擬目標(biāo)方法返回值:" + rvt); System.out.println("模擬記錄日志功能..."); } }
?
?
(5)、定義 AfterThrowing 增強(qiáng)處理。
?
?
- //?定義一個切面 ??
- @Aspect ??
- public ? class ?AfterThrowingAdviceTest?{??
- ???? //?匹配?com.wicresoft.app.service.impl?包下所有類的所有方法作為切入點 ??
- ???? @AfterThrowing (throwing= "ex" ,?pointcut= "execution(*?com.wicresoft.app.service.impl.*.*(..))" )??
- ???? public ? void ?doRecoverActions(Throwable?ex)?{??
- ????????System.out.println( "目標(biāo)方法中拋出的異常:" ?+?ex);??
- ????????System.out.println( "模擬拋出異常后的增強(qiáng)處理..." );??
- ????}??
- }??
// 定義一個切面 @Aspect public class AfterThrowingAdviceTest { // 匹配 com.wicresoft.app.service.impl 包下所有類的所有方法作為切入點 @AfterThrowing(throwing="ex", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))") public void doRecoverActions(Throwable ex) { System.out.println("目標(biāo)方法中拋出的異常:" + ex); System.out.println("模擬拋出異常后的增強(qiáng)處理..."); } }
?
?
(6)、定義 After 增強(qiáng)處理。
?
After 增強(qiáng)處理與AfterReturning?增強(qiáng)處理有點相似,但也有區(qū)別:
?
-
AfterReturning?增強(qiáng)處理處理只有在目標(biāo)方法成功完成后才會被織入。
- After 增強(qiáng)處理不管目標(biāo)方法如何結(jié)束(保存成功完成和遇到異常中止兩種情況),它都會被織入。
?
?
- //?定義一個切面 ??
- @Aspect ??
- public ? class ?AfterAdviceTest?{??
- ???? //?匹配?com.wicresoft.app.service.impl?包下所有類的所有方法作為切入點 ??
- ???? @After ( "execution(*?com.wicresoft.app.service.impl.*.*(..))" )??
- ???? public ? void ?release()?{??
- ????????System.out.println( "模擬方法結(jié)束后的釋放資源..." );??
- ????}??
- }??
// 定義一個切面 @Aspect public class AfterAdviceTest { // 匹配 com.wicresoft.app.service.impl 包下所有類的所有方法作為切入點 @After("execution(* com.wicresoft.app.service.impl.*.*(..))") public void release() { System.out.println("模擬方法結(jié)束后的釋放資源..."); } }
?
?
(7)、Around 增強(qiáng)處理
?
Around 增強(qiáng)處理近似等于 Before 增強(qiáng)處理和 ?AfterReturning 增強(qiáng)處理的總和。它可改變執(zhí)行目標(biāo)方法的參數(shù)值,也可改變目標(biāo)方法之后的返回值。
?
- //?定義一個切面 ??
- @Aspect ??
- public ? class ?AroundAdviceTest?{??
- ???? //?匹配?com.wicresoft.app.service.impl?包下所有類的所有方法作為切入點 ??
- ???? @Around ( "execution(*?com.wicresoft.app.service.impl.*.*(..))" )??
- ???? public ?Object?processTx(ProceedingJoinPoint?jp)? throws ?java.lang.Throwable?{??
- ????????System.out.println( "執(zhí)行目標(biāo)方法之前,模擬開始事物..." );??
- ???????? //?執(zhí)行目標(biāo)方法,并保存目標(biāo)方法執(zhí)行后的返回值 ??
- ????????Object?rvt?=?jp.proceed( new ?String[]{ "被改變的參數(shù)" });??
- ????????System.out.println( "執(zhí)行目標(biāo)方法之前,模擬結(jié)束事物..." );??
- ???????? return ?rvt?+? "新增的內(nèi)容" ;??
- ????}??
- }??
// 定義一個切面 @Aspect public class AroundAdviceTest { // 匹配 com.wicresoft.app.service.impl 包下所有類的所有方法作為切入點 @Around("execution(* com.wicresoft.app.service.impl.*.*(..))") public Object processTx(ProceedingJoinPoint jp) throws java.lang.Throwable { System.out.println("執(zhí)行目標(biāo)方法之前,模擬開始事物..."); // 執(zhí)行目標(biāo)方法,并保存目標(biāo)方法執(zhí)行后的返回值 Object rvt = jp.proceed(new String[]{"被改變的參數(shù)"}); System.out.println("執(zhí)行目標(biāo)方法之前,模擬結(jié)束事物..."); return rvt + "新增的內(nèi)容"; } }
?
?
(8)、訪問目標(biāo)方法的參數(shù)。
?
訪問目標(biāo)方法最簡單的做法是定義增強(qiáng)處理方法時將第一個參數(shù)定義為 JoinPoint 類型,當(dāng)該增強(qiáng)處理方法被調(diào)用時,該 JoinPoint 參數(shù)就代表了織入增強(qiáng)處理的連接點。JoinPoint 里包含了如下幾個常用方法。
?
- Object[] getArgs(): 返回執(zhí)行目標(biāo)方法時的參數(shù)。
- Signature getSignature(): 返回被增強(qiáng)的方法的相關(guān)信息。
- Object getTarget(): 返回被織入增強(qiáng)處理的目標(biāo)對象。
- Object getThis(): 返回 AOP 框架為目標(biāo)對象生成的代理對象。
?
提示 : 當(dāng)時使用 Around 處理時,我們需要將第一個參數(shù)定義為 ProceedingJoinPoint 類型,該類型是 JoinPoint 類型的子類 。
?
(9)、定義切入點。
所謂切入點,其實質(zhì)就是為一個切入點表達(dá)式起一個名稱,從而允許在多個增強(qiáng)處理中重用該名稱。
Spring 切入點定義包含兩個部分:
?
- 一個切入點表達(dá)式。
- 一個包含名字和任意參數(shù)的方法簽名。
?
?
- //?使用@Pointcut?Annotation?時指定切入點表達(dá)式 ??
- @pointcut ( "execution?*?transfer(..)" )??
- //?使用一個返回值為void,方法體為空的方法來命名切入點 ??
- private ? void ?anyOldTransfer(){}??
- ??
- //?使用上面定義的切入點 ??
- @AfterReturning (pointcut= "anyOldTransfer()" ,?returning= "reVal" )??
- public ? void ?writeLog(String?msg,?Object?reVal){??
- ????...??
- }??
// 使用@Pointcut Annotation 時指定切入點表達(dá)式 @pointcut("execution * transfer(..)") // 使用一個返回值為void,方法體為空的方法來命名切入點 private void anyOldTransfer(){} // 使用上面定義的切入點 @AfterReturning(pointcut="anyOldTransfer()", returning="reVal") public void writeLog(String msg, Object reVal){ ... }
?
?
2、基于 XML 配置文件的管理方式。
?
?
- 不配置切入點
?
?
- <?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:aop= "http://www.springframework.org/schema/aop" ????????
- ???????xsi:schemaLocation="http: //www.springframework.org/schema/beans ??
- ???????????http: //www.springframework.org/schema/beans/spring-beans-3.0.xsd ??
- ???????????http: //www.springframework.org/schema/aop ??
- ???????http: //www.springframework.org/schema/beans/spring-aop-3.0.xsd"> ??
- ????????<aop:config>??
- ????????????<!--?將?fourAdviceBean?轉(zhuǎn)換成切面?Bean,?切面?Bean?的新名稱為:fourAdviceAspect,指定該切面的優(yōu)先級為 2 ?-->??
- ????????????<aop:aspect?id= "fourAdviceAspect" ?ref= "fourAdviceBean" ?order= "2" >??
- ????????????????<!--?定義個After增強(qiáng)處理,直接指定切入點表達(dá)式,以切面?Bean?中的?Release()?方法作為增強(qiáng)處理方法?-->??
- ????????????????<aop:after?pointcut= "execution(*?com.wicresoft.app.service.impl.*.*(..))" ?method= "release" ?/>??
- ??????????????????
- ????????????????<!--?定義個Before增強(qiáng)處理,直接指定切入點表達(dá)式,以切面?Bean?中的?authority()?方法作為增強(qiáng)處理方法?-->??
- ????????????????<aop:before?pointcut= "execution(*?com.wicresoft.app.service.impl.*.*(..))" ?method= "authority" ?/>??
- ??????????????????
- ????????????????<!--?定義個AfterReturning增強(qiáng)處理,直接指定切入點表達(dá)式,以切面?Bean?中的?log()?方法作為增強(qiáng)處理方法?-->??
- ????????????????<aop:after-returning?pointcut= "execution(*?com.wicresoft.app.service.impl.*.*(..))" ?method= "log" ?/>??
- ??????????????????
- ????????????????<!--?定義個Around增強(qiáng)處理,直接指定切入點表達(dá)式,以切面?Bean?中的?processTx()?方法作為增強(qiáng)處理方法?-->??
- ????????????????<aop:around?pointcut= "execution(*?com.wicresoft.app.service.impl.*.*(..))" ?method= "processTx" ?/>??
- ??????????????????
- ????????????</aop:aspect>??
- ????????</aop:config>??
- ??????????
- ????????<!--?省略各個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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/beans/spring-aop-3.0.xsd"> <aop:config> <!-- 將 fourAdviceBean 轉(zhuǎn)換成切面 Bean, 切面 Bean 的新名稱為:fourAdviceAspect,指定該切面的優(yōu)先級為2 --> <aop:aspect id="fourAdviceAspect" ref="fourAdviceBean" order="2"> <!-- 定義個After增強(qiáng)處理,直接指定切入點表達(dá)式,以切面 Bean 中的 Release() 方法作為增強(qiáng)處理方法 --> <aop:after pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="release" /> <!-- 定義個Before增強(qiáng)處理,直接指定切入點表達(dá)式,以切面 Bean 中的 authority() 方法作為增強(qiáng)處理方法 --> <aop:before pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="authority" /> <!-- 定義個AfterReturning增強(qiáng)處理,直接指定切入點表達(dá)式,以切面 Bean 中的 log() 方法作為增強(qiáng)處理方法 --> <aop:after-returning pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="log" /> <!-- 定義個Around增強(qiáng)處理,直接指定切入點表達(dá)式,以切面 Bean 中的 processTx() 方法作為增強(qiáng)處理方法 --> <aop:around pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="processTx" /> </aop:aspect> </aop:config> <!-- 省略各個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:aop= "http://www.springframework.org/schema/aop" ????????
- ???????xsi:schemaLocation="http: //www.springframework.org/schema/beans ??
- ???????????http: //www.springframework.org/schema/beans/spring-beans-3.0.xsd ??
- ???????????http: //www.springframework.org/schema/aop ??
- ???????http: //www.springframework.org/schema/beans/spring-aop-3.0.xsd"> ??
- ????????<aop:config>??
- ????????????<!--?定義一個切入點,myPointcut,直接知道它對應(yīng)的切入點表達(dá)式?-->??
- ????????????<aop:pointcut?id= "myPointcut" ?expression= "execution(*?com.wicresoft.app.service.impl.*.*(..))" ?method= "release" ?/>??
- ????????????<aop:aspect?id= "afterThrowingAdviceAspect" ?ref= "afterThrowingAdviceBean" ?order= "1" >??
- ????????????????<!--?使用上面定于切入點定義增強(qiáng)處理?-->??
- ????????????????<!--?定義一個AfterThrowing?增強(qiáng)處理,指定切入點以切面?Bean?中的?doRecovertyActions()?方法作為增強(qiáng)處理方法?-->??
- ????????????????<aop:after-throwing?pointcut-ref= "myPointcut" ?method= "doRecovertyActions" ?throwing= "ex" ?/>??
- ????????????</aop:aspect>??
- ????????</aop:config>??
- ??????????
- ????????<!--?省略各個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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/beans/spring-aop-3.0.xsd"> <aop:config> <!-- 定義一個切入點,myPointcut,直接知道它對應(yīng)的切入點表達(dá)式 --> <aop:pointcut id="myPointcut" expression="execution(* com.wicresoft.app.service.impl.*.*(..))" method="release" /> <aop:aspect id="afterThrowingAdviceAspect" ref="afterThrowingAdviceBean" order="1"> <!-- 使用上面定于切入點定義增強(qiáng)處理 --> <!-- 定義一個AfterThrowing 增強(qiáng)處理,指定切入點以切面 Bean 中的 doRecovertyActions() 方法作為增強(qiáng)處理方法 --> <aop:after-throwing pointcut-ref="myPointcut" method="doRecovertyActions" throwing="ex" /> </aop:aspect> </aop:config> <!-- 省略各個Bean 的配置 --> <!-- ... --> </beans>
?
參考:
《輕量級 Java EE 企業(yè)應(yīng)用實戰(zhàn)(第三版)》 李剛
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
