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