在軟件開(kāi)發(fā)中,分布于應(yīng)用多處的功能被稱(chēng)為橫切關(guān)注點(diǎn)。將這些橫切關(guān)注點(diǎn)和業(yè)務(wù)邏輯分離是面向切面鎖要解決的問(wèn)題。橫切關(guān)注點(diǎn)可以被模塊化為特殊的類(lèi),這些類(lèi)就叫做切面。這樣做的好處:每個(gè)關(guān)注點(diǎn)集中于一處,而不是分散到多出代碼中。服務(wù)模塊更簡(jiǎn)潔,它們只要關(guān)心核心功能就行了,其他轉(zhuǎn)移到切面中去了。
1.1定義AOP術(shù)語(yǔ)
描述aop的常用術(shù)語(yǔ)有通知(advice)、切點(diǎn)(pointcut)、和連接點(diǎn)(join point)。
通知:
通知定義了切面是什么以及何時(shí)執(zhí)行,有5中通知類(lèi)型。
1.Before:在方法執(zhí)行前調(diào)用通知。
2.After:在方法執(zhí)行后調(diào)用通知,不管方法執(zhí)行是否成功。
3.After-returning:在方法成功執(zhí)行后調(diào)用通知。
4.After-throwing:在方法拋出異常時(shí)調(diào)用。
5.Around:通知包含了被通知的方法,再被通知的方法調(diào)用之前和之后執(zhí)行相應(yīng)的自定義工作。
連接點(diǎn):連接點(diǎn)就是應(yīng)用執(zhí)行過(guò)程中能夠插入切面的一個(gè)點(diǎn)。這個(gè)點(diǎn)可以是調(diào)用方法時(shí)、調(diào)用方法后、拋出異常時(shí)等等,切面代碼可以利用這些點(diǎn)插入到應(yīng)用程序的正常流程中,并添加新的行為。
切點(diǎn):如果通知定義了什么和何時(shí),那么切點(diǎn)就定義了何處。切點(diǎn)有助于縮小連接點(diǎn)的范圍,因?yàn)橐粋€(gè)切面不需要通知所有的連接點(diǎn)。
切面(aspect):通知和切點(diǎn)的結(jié)合。它們定義了切面是什么,在何時(shí)何處執(zhí)行。
引入(Introduction):引入允許向現(xiàn)有的類(lèi)添加新方法或?qū)傩浴?/p>
織入(weaving):織入是將切面應(yīng)用到目標(biāo)對(duì)象來(lái)生成心得代理對(duì)象的過(guò)程。切面在指定的連接點(diǎn)中被織入目標(biāo)對(duì)象。目標(biāo)對(duì)象的生命周期中有多個(gè)點(diǎn)可以進(jìn)行織入。
1.編譯期:切面在目標(biāo)類(lèi)編譯時(shí)被織入。這種方式需要特殊的編譯器,AspectJ的織入編譯器就是這個(gè)時(shí)候織入的。
2.類(lèi)加載期:切面在類(lèi)加載到j(luò)vm是被織入。這種方式需要特殊的類(lèi)加載器,它可以再目標(biāo)類(lèi)被引入應(yīng)用之前加強(qiáng)目標(biāo)類(lèi)的字節(jié)碼。AspectJ的LTW就是這種方式織入的。
3.運(yùn)行期:切面在應(yīng)用運(yùn)行到某一時(shí)刻時(shí)被織入。一般情況下,在織入切面時(shí),AOP容器會(huì)為目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象。SPRing AOP就是這種方式織入的。
1.2 Spring對(duì)AOP的支持
并不是所有的aop框架都是一樣的,它們?cè)谶B接點(diǎn)模型上有強(qiáng)弱之分。有三個(gè)AOP框架:AspectJ,JBoss AOP,Spring AOP。
spring提供4中具有特殊的aop支持:
1.基于代理的經(jīng)典AOP。
2.@AspectJ注解驅(qū)動(dòng)的切面。
3.純POJO切面。
4.注入式AspectJ切面(適合spring個(gè)版本)
前三種方法都是基于代理的aop的變種,spring對(duì)aop的支持局限于方法的攔截。如果對(duì)aop的需求超過(guò)了對(duì)簡(jiǎn)單方法的攔截(比如對(duì)構(gòu)造方法或?qū)傩詳r截),那么需要在AspectJ里實(shí)現(xiàn)切面,利用依賴注入把Spring Bean注入到切面中。
2.使用切點(diǎn)選擇連接點(diǎn)
spring支持的AspectJ切點(diǎn)指示器。
args()---------限制連接點(diǎn)匹配參數(shù)為指定類(lèi)型的執(zhí)行方法。
@args()---- 限制連接點(diǎn)匹配參數(shù)為指定注解標(biāo)注的執(zhí)行方法。
execution() 用于匹配時(shí)連接點(diǎn)的執(zhí)行方法
this()--------- 限制連接點(diǎn)匹配AOP代理的Bean的引用為指定類(lèi)型的類(lèi)
target()-------限制連接點(diǎn)匹配目標(biāo)對(duì)象為指定類(lèi)型的類(lèi)
@target() 限制連接點(diǎn)匹配特定的執(zhí)行對(duì)象,這些對(duì)象對(duì)應(yīng)的類(lèi)要有指定的注解
within() 限制連接點(diǎn)匹配指定的類(lèi)型
@within() 限制連接點(diǎn)匹配注解所標(biāo)注的類(lèi)型
@annotation 限制匹配帶有指定注解的連接點(diǎn)
在Spring使用其他AspectJ指示器時(shí)會(huì)報(bào)illegalArgumentException。只有execution是唯一的執(zhí)行匹配,其他都是限制匹配。
2.1編寫(xiě)切點(diǎn)
execution(* com.xxx.A.get(..))上面第一個(gè)*表示返回類(lèi)型,*表示不關(guān)心返回類(lèi)型,隨便什么返回類(lèi)型都可以。然后是類(lèi)的全限定名和方法名。(..)表示使用任意參數(shù)都可以,就是說(shuō)所有的get()方法。execution(xxx) && within(xxx) || execution(xxx) && arg(xxx) ||!execution(xxx) 2.2使用spring的bean指示器這個(gè)指示器允許在切點(diǎn)表達(dá)式中使用Bean的id來(lái)識(shí)別Bean。
execution(xxx) && bean(id)3 在xml中聲明切面<aop:advisor>:定義AOP通知器
<aop:after>:定義AOP后置通知
<aop:after-returning>:定義AOP after-returning通知
<aop:after-throwing>:定義AOP after-throwing通知
<aop:around>:定義AOP環(huán)繞通知
<aop:aspect>:定義切面
<aop:aspectj-autoproxy>:?jiǎn)?dòng)@AspectJ注解驅(qū)動(dòng)的切面
<aop:before>:定義前置通知
<aop:config>:頂層的aop配置元素,大多數(shù)<aop:*>都在這里面
<aop:declear-parents>:為被通知的對(duì)象引入額外的接口,并透明的實(shí)現(xiàn)。
<aop:pointcut>:定義切點(diǎn)
<aop:config> <aop:aspect id="bean的id">//定義切面 <aop:pointcut id="切點(diǎn)id" expression="execution()"/>//定義切點(diǎn) <aop:before pointcut-ref="上面的切點(diǎn)id" method="bean里面的方法名"/>//定義前置通知 </aop:aspect></aop:config>如果pointcut想被多個(gè)切面引用,就把pointcut定義在config中。3.1 聲明環(huán)繞通知
<aop:around pointcut-ref="" method=""/> 環(huán)繞通知就是把前置通知和后置通知合起來(lái)變?yōu)橐粋€(gè)方法。如果前置通知和后置通知有聯(lián)系,那么可以用環(huán)繞通知來(lái)實(shí)現(xiàn),環(huán)繞通知的方法參數(shù)要有被通知的對(duì)象,這樣可以調(diào)用被通知的方法。3.2為通知傳遞參數(shù)
<aop:before pointcut-ref="" method="" arg-names="name" />3.3 通過(guò)切面引入新功能<aop:aspect> <aop:declear-parents types-matching=""//匹配類(lèi) implement-interface=""//實(shí)現(xiàn)的接口 default-impl=""//接口的實(shí)現(xiàn)類(lèi) delegate-ref=""//引入接口實(shí)現(xiàn)類(lèi)的Bean id和上面的區(qū)別就是它是引用外部的一個(gè)Bean/></aop:aspect>4.注解切面@AspectJ//定義切面class A{@Pointcut("execution()")//定義切點(diǎn) public void a(){} @Before("a()") public void before(){System.out.println("before")} @AfterReturning("a()") public void afterReturning(){System.out.pringln("afterReturning")}}然后要在xml中加上<aop:aspectj-autoproxy/>,這個(gè)元素僅僅使用@AspectJ注解作為指引來(lái)創(chuàng)建基于代理的切面。本質(zhì)還是spring風(fēng)格的切面。
<aop:aspect>和@AspectJ注解都是把一個(gè)POJO轉(zhuǎn)變?yōu)橐粋€(gè)切面的有效方式。<aop:aspect>可以引用任意一個(gè)Bean,而注解就要實(shí)現(xiàn)一個(gè)類(lèi)。
4.1注解環(huán)繞通知@Around("a()")public void around(ProceedingJoinPoint a){ a.proceed();}@DeclearParents(value=""defaultImpl="")private A a;
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注