首先我們來(lái)看一下官方文檔所給我們的關(guān)于AOP的一些概念性詞語(yǔ)的解釋:
切面(Aspect):一個(gè)關(guān)注點(diǎn)的模塊化,這個(gè)關(guān)注點(diǎn)可能會(huì)橫切多個(gè)對(duì)象。事務(wù)管理是J2EE應(yīng)用中一個(gè)關(guān)于橫切關(guān)注點(diǎn)的很好的例子。在spring?AOP中,切面可以使用基于模式)或者基于Aspect注解方式來(lái)實(shí)現(xiàn)。通俗點(diǎn)說(shuō)就是我們加入的切面類(比如log類),可以這么理解。
連接點(diǎn)(Joinpoint):在程序執(zhí)行過程中某個(gè)特定的點(diǎn),比如某方法調(diào)用的時(shí)候或者處理異常的時(shí)候。在Spring?AOP中,一個(gè)連接點(diǎn)總是表示一個(gè)方法的執(zhí)行。通俗的說(shuō)就是加入切點(diǎn)的那個(gè)點(diǎn)
通知(Advice):在切面的某個(gè)特定的連接點(diǎn)上執(zhí)行的動(dòng)作。其中包括了“around”、“before”和“after”等不同類型的通知(通知的類型將在后面部分進(jìn)行討論)。許多AOP框架(包括Spring)都是以攔截器做通知模型,并維護(hù)一個(gè)以連接點(diǎn)為中心的攔截器鏈。
切入點(diǎn)(Pointcut):匹配連接點(diǎn)的斷言。通知和一個(gè)切入點(diǎn)表達(dá)式關(guān)聯(lián),并在滿足這個(gè)切入點(diǎn)的連接點(diǎn)上運(yùn)行(例如,當(dāng)執(zhí)行某個(gè)特定名稱的方法時(shí))。切入點(diǎn)表達(dá)式如何和連接點(diǎn)匹配是AOP的核心:Spring缺省使用AspectJ切入點(diǎn)語(yǔ)法。
引入(Introduction):用來(lái)給一個(gè)類型聲明額外的方法或?qū)傩?#xff08;也被稱為連接類型聲明(inter-type?declaration))。Spring允許引入新的接口(以及一個(gè)對(duì)應(yīng)的實(shí)現(xiàn))到任何被代理的對(duì)象。例如,你可以使用引入來(lái)使一個(gè)bean實(shí)現(xiàn)IsModified接口,以便簡(jiǎn)化緩存機(jī)制。
目標(biāo)對(duì)象(Target?Object):?被一個(gè)或者多個(gè)切面所通知的對(duì)象。也被稱做被通知(advised)對(duì)象。?既然Spring?AOP是通過運(yùn)行時(shí)代理實(shí)現(xiàn)的,這個(gè)對(duì)象永遠(yuǎn)是一個(gè)被代理(proxied)對(duì)象。
AOP代理(AOP?Proxy):AOP框架創(chuàng)建的對(duì)象,用來(lái)實(shí)現(xiàn)切面契約(例如通知方法執(zhí)行等等)。在Spring中,AOP代理可以是JDK動(dòng)態(tài)代理或者CGLIB代理。
織入(Weaving):把切面連接到其它的應(yīng)用程序類型或者對(duì)象上,并創(chuàng)建一個(gè)被通知的對(duì)象。這些可以在編譯時(shí)(例如使用AspectJ編譯器),類加載時(shí)和運(yùn)行時(shí)完成。Spring和其他純Java?AOP框架一樣,在運(yùn)行時(shí)完成織入。
通知類型:
前置通知(Before?advice):在某連接點(diǎn)之前執(zhí)行的通知,但這個(gè)通知不能阻止連接點(diǎn)之前的執(zhí)行流程(除非它拋出一個(gè)異常)。
后置通知(After?returning?advice):在某連接點(diǎn)正常完成后執(zhí)行的通知:例如,一個(gè)方法沒有拋出任何異常,正常返回。
異常通知(After?throwing?advice):在方法拋出異常退出時(shí)執(zhí)行的通知。
最終通知(After?(finally)?advice):當(dāng)某連接點(diǎn)退出的時(shí)候執(zhí)行的通知(不論是正常返回還是異常退出)。
環(huán)繞通知(Around?Advice):包圍一個(gè)連接點(diǎn)的通知,如方法調(diào)用。這是最強(qiáng)大的一種通知類型。環(huán)繞通知可以在方法調(diào)用前后完成自定義的行為。它也會(huì)選擇是否繼續(xù)執(zhí)行連接點(diǎn)或直接返回它自己的返回值或拋出異常來(lái)結(jié)束執(zhí)行。
環(huán)繞通知是最常用的通知類型。和AspectJ一樣,Spring提供所有類型的通知,我們推薦你使用盡可能簡(jiǎn)單的通知類型來(lái)實(shí)現(xiàn)需要的功能。例如,如果你只是需要一個(gè)方法的返回值來(lái)更新緩存,最好使用后置通知而不是環(huán)繞通知,盡管環(huán)繞通知也能完成同樣的事情。用最合適的通知類型可以使得編程模型變得簡(jiǎn)單,并且能夠避免很多潛在的錯(cuò)誤。比如,你不需要在JoinPoint上調(diào)用用于環(huán)繞通知的proceed()方法,就不會(huì)有調(diào)用的問題。
spring?AOP的實(shí)現(xiàn)
??????在spring2.5中,常用的AOP實(shí)現(xiàn)方式有兩種。第一種是基于xml配置文件方式的實(shí)現(xiàn),第二種是基于注解方式的實(shí)現(xiàn)。接下來(lái),以具體的示例來(lái)講解這兩種方式的使用。下面我們要用到的實(shí)例是一個(gè)注冊(cè),就有用戶名和密碼,我們利用AOP來(lái)實(shí)現(xiàn)在用戶注冊(cè)的時(shí)候?qū)崿F(xiàn)在保存數(shù)據(jù)之前和之后或者是拋出異常時(shí),在這些情況下都給他加上日志。在這里我們只講解AOP,所以我只把關(guān)鍵代碼貼出來(lái),不相干的就不貼了。
首先我們來(lái)看一下業(yè)務(wù)邏輯service層:
[java]?view plaincopy print?
? ? ?? public?class?RegisterServiceImpl?implements?RegisterService?{?? ????private??RegisterDao?registerDao;?? ????public?RegisterServiceImpl()?{}?? ?????? ????public?RegisterServiceImpl(RegisterDao??registerDao){?? ????????this.registerDao?=registerDao;?? ????}?? ????public?void?save(String?loginname,?String?password)?{?? ????????registerDao.save(loginname,?password);?? ????????throw?new?RuntimeException("故意拋出一個(gè)異常。。。。");?? ????}?? ???????? ????public?void?setRegisterDao(RegisterDao?registerDao)?{?? ????????this.registerDao?=?registerDao;?? }}??
對(duì)于業(yè)務(wù)系統(tǒng)來(lái)說(shuō),RegisterServiceImpl類就是目標(biāo)實(shí)現(xiàn)類,它的業(yè)務(wù)方法,如save()方法的前后或代碼會(huì)出現(xiàn)異常的地方都是AOP的連接點(diǎn)。
?
下面是日志服務(wù)類的代碼:
[java]?view plaincopy print?
? ? ? ?? public?class?LogAspect?{?? ?????? ????public?void?before(JoinPoint?call)?{?? ?????????? ????????String?className?=?call.getTarget().getClass().getName();?? ?????????? ????????String?methodName?=?call.getSignature().getName();?? ????????System.out.println("前置通知:"?+?className?+?"類的"?+?methodName?+?"方法開始了");?? ????}?? ????public?void?afterReturn()?{?? ????????System.out.println("后置通知:方法正常結(jié)束了");?? ????}?? ????public?void?after(){?? ????????System.out.println("最終通知:不管方法有沒有正常執(zhí)行完成,一定會(huì)返回的");?? ????}?? ????public?void?afterThrowing()?{?? ????????System.out.println("異常拋出后通知:方法執(zhí)行時(shí)出異常了");?? ????}?? ?????? ????public?Object?doAround(ProceedingJoinPoint?call)?throws?Throwable?{?? ????????Object?result?=?null;?? ????????this.before(call);?? ????????try?{?? ????????????result?=?call.proceed();?? ????????????this.afterReturn();??? ????????}?catch?(Throwable?e)?{?? ????????????this.afterThrowing();???? ????????????throw?e;?? ????????}finally{?? ????????????this.after();???? ????????}?? ????????return?result;?? ????}?? }??
?????這個(gè)類屬于業(yè)務(wù)服務(wù)類,如果用AOP的術(shù)語(yǔ)來(lái)說(shuō),它就是一個(gè)切面類,它定義了許多通知。Before()、afterReturn()、after()和afterThrowing()這些方法都是通知。
?
下面我們就來(lái)看具體配置,首先來(lái)看一下:
<1>.基于xml配置文件的AOP實(shí)現(xiàn):這種方式在實(shí)現(xiàn)AOP時(shí),有4個(gè)步驟。
?
[html]?view plaincopy print?
<?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-2.5.xsd?? ????????????http://www.springframework.org/schema/aop?http://www.springframework.org/schema/aop/spring-aop-2.5.xsd>?? ????<bean?id="registerDaoImpl"?class="com.zxf.dao.RegisterDaoImpl"/>?? ????<bean?id="registerService"?class="com.zxf.service.RegisterServiceImpl">?? ????????<property?name="?registerDaoImpl?"?ref="?RegisterDaoImpl?"/>?? ????</bean>?? ?????? ????<bean?id="logAspectBean"?class="com.zxf.aspect.LogAspect"/>?? ?????? ????<aop:config>?? ?????????? ????????<aop:aspect?id="logAspect"?ref="logAspectBean">?? ?????????????? ????????????<aop:pointcut?id="allMethod"??? ????????????????expression="execution(*?com.zxf.service.*.*(..))"/>??? ?????????????? ????????????<aop:before?method="before"?pointcut-ref="allMethod"?/>?? ?????????????? ????????????<aop:after-returning?method="afterReturn"?pointcut-ref="allMethod"/>?? ?????????????? ????????????<aop:after?method="after"?pointcut-ref="allMethod"/>?? ?????????????? ????????????<aop:after-throwing?method="afterThrowing"?pointcut-ref="allMethod"/>?? ?????????????? ????????????? ? ?? ????????</aop:aspect>?? ????</aop:config>?? </beans>??
????上述配置針對(duì)切入點(diǎn)應(yīng)用了前置、后置、最終,以及拋出異常后通知。這樣在測(cè)試執(zhí)行RegisterServiceImpl類的save()方法時(shí),控制臺(tái)會(huì)有如下結(jié)果輸出:
?
前置通知:com.zxf.service.RegisterServiceImpl類的save方法開始了。
針對(duì)MySQL的RegisterDao實(shí)現(xiàn)中的save()方法。
后置通知:方法正常結(jié)束了。
最終通知:不管方法有沒有正常執(zhí)行完成,一定會(huì)返回的。
下面我們?cè)趤?lái)看一下第二種配置方式:
<2>基于注解的AOP的實(shí)現(xiàn)
?
?????首先創(chuàng)建一個(gè)用來(lái)作為切面的類LogAnnotationAspect,同時(shí)把這個(gè)類配置在spring的配置文件中。
????????在spring2.0以后引入了JDK5.0的注解Annotation的支持,提供了對(duì)AspectJ基于注解的切面的支持,從而?更進(jìn)一步地簡(jiǎn)化AOP的配置。具體的步驟有兩步。
?
Spring的配置文件是如下的配置:
?
[html]?view plaincopy print?
<?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-2.5.xsd?? ????????????http://www.springframework.org/schema/aop?http://www.springframework.org/schema/aop/spring-aop-2.5.xsd>?? ????<bean?id="registerDao"?class="com.zxf.dao.RegisterDaoImpl"/>?? ????<bean?id="registerService"?class="com.zxf.service.RegisterServiceImpl">?? ????????<property?name="registerDao"?ref="registerDao"/>?? ????</bean>?? ?????? ????<bean?id="logAspectBean"?class="com.zxf.aspect.LogAnnotationAspect"/>?? ?????? ????<aop:aspectj-autoproxy/>?? </beans>??
?
這是那個(gè)切面的類LogAnnotationAspect
[java]?view plaincopy print?
? ? ?? @Aspect???? public?class?LogAnnotationAspect?{?? ????@SuppressWarnings("unused")?? ?????? ????@Pointcut("execution(*?com.zxf.service.*.*(..))")?? ????private?void?allMethod(){}?? ?????? ????@Before("execution(*?com.?zxf.service.*.*(..))")?? ????public?void?before(JoinPoint?call)?{?? ????????String?className?=?call.getTarget().getClass().getName();?? ????????String?methodName?=?call.getSignature().getName();?? ????????System.out.println("【注解-前置通知】:"?+?className?+?"類的"??? ????????????????+?methodName?+?"方法開始了");?? ????}?? ?????? ????@AfterReturning("allMethod()")?? ????public?void?afterReturn()?{?? ????????System.out.println("【注解-后置通知】:方法正常結(jié)束了");?? ????}?? ?????? ????@After("allMethod()")?? ????public?void?after(){?? ????????System.out.println("【注解-最終通知】:不管方法有沒有正常執(zhí)行完成,"??? ????????????????+?"一定會(huì)返回的");?? ????}?? ?????? ????@AfterThrowing("allMethod()")?? ????public?void?afterThrowing()?{?? ????????System.out.println("【注解-異常拋出后通知】:方法執(zhí)行時(shí)出異常了");?? ????}?? ?????? ?????? ????public?Object?doAround(ProceedingJoinPoint?call)?throws?Throwable{?? ????????Object?result?=?null;?? ????????this.before(call);?? ????????try?{?? ????????????result?=?call.proceed();?? ????????????this.afterReturn();??? ????????}?catch?(Throwable?e)?{?? ????????????this.afterThrowing();???? ????????????throw?e;?? ????????}finally{?? ????????????this.after();???? ????????}?? ????????return?result;?? ????}?? }??
?備注:輸出結(jié)果和前面的一樣。
from:?http://blog.csdn.net/csh624366188/article/details/7651702
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀
總結(jié)
以上是生活随笔為你收集整理的Java程序员从笨鸟到菜鸟之(七十四)细谈Spring(六)spring之AOP基本概念和配置详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。