Spring框架——AOP(面向切面編程)詳解

 1 AOP概述

AOP(Aspect-Oriented Programming面向切面編程):是一種新的方法論,是對傳統 OOP(Object-Oriented Programming,面向對象編程)的補充。java

AOP編程操做的主要對象是切面(aspect),而切面模塊化橫切關注點spring

●在應用AOP編程時,仍然須要定義公共功能,但能夠明確的定義這個功能應用在哪裏,以什麼方式應用,而且沒必要修改受影響的類。這樣一來橫切關注點就被模塊化到特殊的類裏——這樣的類咱們一般稱之爲「切面」。數據庫

AOP的好處:express

○每一個事物邏輯位於一個位置,代碼不分散,便於維護和升級編程

○業務模塊更簡潔,只包含核心業務代碼框架

 

 

 

2 AOP術語

2.1 橫切關注點

  從每一個方法中抽取出來的同一類非核心業務。(抽離到方法中處理非核心業務)模塊化

2.2 切面(Aspect)

  封裝橫切關注點信息的類,每一個關注點體現爲一個通知方法。ui

2.3 通知(Advice)

  切面必需要完成的各個具體工做spa

2.4 目標(Target)

  被通知的對象代理

2.5 代理(Proxy)

  向目標對象應用通知以後建立的代理對象

2.6 鏈接點(Joinpoint)

  橫切關注點在程序代碼中的具體體現,對應程序執行的某個特定位置。例如:類某個方法調用前、調用後、方法捕獲到異常後等。

  在應用程序中可使用橫縱兩個座標來定位一個具體的鏈接點:

2.7 切入點(pointcut)

  定位鏈接點的方式。每一個類的方法中都包含多個鏈接點,因此鏈接點是類中客觀存在的事物。若是把鏈接點看做數據庫中的記錄,那麼切入點就是查詢條件——AOP能夠經過切入點定位到特定的鏈接點。

  切點經過org.springframework.aop.Pointcut 接口進行描述,它使用類和方法做爲鏈接點的查詢條件。

 

3 AspectJ

3.1 簡介

AspectJJava社區裏最完整最流行的AOP框架。

Spring2.0以上版本中,可使用基於AspectJ註解或基於XML配置的AOP

3.2 Spring中啓用AspectJ註解支持

①導入JAR

aopalliance.jar

aspectj.weaver.jar

spring-aspects.jar

②引入aop名稱空間

 

 

③配置

<aop:aspectj-autoproxy>

Spring IOC容器偵測到bean配置文件中的<aop:aspectj-autoproxy>元素時,會自動爲與AspectJ切面匹配的bean建立代理

 

3.3 AspectJ註解聲明切面

①要在Spring中聲明AspectJ切面,只須要在IOC容器中將切面聲明爲bean實例。②當在Spring IOC容器中初始化AspectJ切面以後,Spring IOC容器就會爲那些與 AspectJ切面相匹配的bean建立代理。

③在AspectJ註解中,切面只是一個帶有@Aspect註解的Java類,它每每要包含不少通知。

④通知是標註有某種註解的簡單的Java方法。

AspectJ支持5種類型的通知註解:

  [1]@Before:前置通知,在方法執行以前執行

  [2]@After:後置通知,在方法執行以後執行

  [3]@AfterRunning:返回通知,在方法返回結果以後執行

  [4]@AfterThrowing:異常通知,在方法拋出異常以後執行

  [5]@Around:環繞通知,圍繞着方法執行

 

 

4 切入點表達式

4.1 做用

經過表達式的方式定位一個或多個具體的鏈接點。

4.2 語法細節

①切入點表達式的語法格式

execution([權限修飾符] [返回值類型] [簡單類名/全類名] [方法名]([參數列表]))

②舉例說明

表達式

execution(* com.atguigu.spring.ArithmeticCalculator.*(..))

含義

ArithmeticCalculator接口中聲明的全部方法。

第一個「*」表明任意修飾符及任意返回值。

第二個「*」表明任意方法。

..」匹配任意數量、任意類型的參數。

若目標類、接口與該切面類在同一個包中能夠省略包名。

 

表達式

execution(public * ArithmeticCalculator.*(..))

含義

ArithmeticCalculator接口的全部公有方法

 

表達式

execution(public double ArithmeticCalculator.*(..))

含義

ArithmeticCalculator接口中返回double類型數值的方法

 

表達式

execution(public double ArithmeticCalculator.*(double, ..))

含義

第一個參數爲double類型的方法。

..」 匹配任意數量、任意類型的參數。

 

表達式

execution(public double ArithmeticCalculator.*(double, double))

含義

參數類型爲doubledouble類型的方法

 

③在AspectJ中,切入點表達式能夠經過 「&&」、「||」、「!」等操做符結合起來。

表達式

execution (* *.add(int,..)) || execution(* *.sub(int,..))

含義

任意類中第一個參數爲int類型的add方法或sub方法

 

4.3 切入點表達式應用到實際的切面類中

 

 

 

5 當前鏈接點細節

5.1 概述

切入點表達式一般都會是從宏觀上定位一組方法,和具體某個通知的註解結合起來就可以肯定對應的鏈接點。那麼就一個具體的鏈接點而言,咱們可能會關心這個鏈接點的一些具體信息,例如:當前鏈接點所在方法的方法名、當前傳入的參數值等等。這些信息都封裝在JoinPoint接口的實例對象中。

5.2 JoinPoint

 

 

6 通知

6.1 概述

l 在具體的鏈接點上要執行的操做。

l 一個切面能夠包括一個或者多個通知。

l 通知所使用的註解的值每每是切入點表達式。

6.2 前置通知

l 前置通知:在方法執行以前執行的通知

使用@Before註解

6.3 後置通知

l 後置通知:後置通知是在鏈接點完成以後執行的,即鏈接點返回結果或者拋出異常的時候

使用@After註解

6.4 返回通知

l 返回通知:不管鏈接點是正常返回仍是拋出異常,後置通知都會執行。若是隻想在鏈接點返回的時候記錄日誌,應使用返回通知代替後置通知。

使用@AfterReturning註解

l 在返回通知中訪問鏈接點的返回值

  • 在返回通知中,只要將returning屬性添加到@AfterReturning註解中,就能夠訪問鏈接點的返回值。該屬性的值即爲用來傳入返回值的參數名稱
  • 必須在通知方法的簽名中添加一個同名參數。在運行時Spring AOP會經過這個參數傳遞返回值
  • 原始的切點表達式須要出如今pointcut屬性中

 

 

6.5 異常通知

l 異常通知:只在鏈接點拋出異常時才執行異常通知

throwing屬性添加到@AfterThrowing註解中,也能夠訪問鏈接點拋出的異常。Throwable是全部錯誤和異常類的頂級父類,因此在異常通知方法能夠捕獲到任何錯誤和異常。

l 若是隻對某種特殊的異常類型感興趣,能夠將參數聲明爲其餘異常的參數類型。而後通知就只在拋出這個類型及其子類的異常時才被執行

 

 

6.6 環繞通知

l 環繞通知是全部通知類型中功能最爲強大的,可以全面地控制鏈接點,甚至能夠控制是否執行鏈接點。

對於環繞通知來講,鏈接點的參數類型必須是ProceedingJoinPoint。它是 JoinPoint的子接口,容許控制什麼時候執行,是否執行鏈接點。

在環繞通知中須要明確調用ProceedingJoinPointproceed()方法來執行被代理的方法。若是忘記這樣作就會致使通知被執行了,但目標方法沒有被執行。

注意:環繞通知的方法須要返回目標方法執行以後的結果,即調用 joinPoint.proceed();的返回值,不然會出現空指針異常。

 

 

6.7 重用切入點定義

在編寫AspectJ切面時,能夠直接在通知註解中書寫切入點表達式。但同一個切點表達式可能會在多個通知中重複出現。

AspectJ切面中,能夠經過@Pointcut註解將一個切入點聲明成簡單的方法。切入點的方法體一般是空的,由於將切入點定義與應用程序邏輯混在一塊兒是不合理的。

切入點方法的訪問控制符同時也控制着這個切入點的可見性。若是切入點要在多個切面中共用,最好將它們集中在一個公共的類中。在這種狀況下,它們必須被聲明爲public。在引入這個切入點時,必須將類名也包括在內。若是類沒有與這個切面放在同一個包中,還必須包含包名。

l 其餘通知能夠經過方法名稱引入該切入點

 

 

6.8 指定切面的優先級

l 在同一個鏈接點上應用不止一個切面時,除非明確指定,不然它們的優先級是不肯定的。

切面的優先級能夠經過實現Ordered接口或利用@Order註解指定。

實現Ordered接口,getOrder()方法的返回值越小,優先級越高。

若使用@Order註解,序號出如今註解中

 

 

6.9注意:

上面的AOP都是經過註解實現的,AOP實際上也能夠經過xml配置的方式實現!

<!-- 1.將須要加載到IOC容器中的bean配置好 -->
<bean id="logAspect" class="com.neuedu.aop.proxy.LogAspect"></bean>
<bean id="txAspect" class="com.neuedu.aop.target.TxAspect"></bean>
<bean id="calculator" class="com.neuedu.aop.target.MathCalculatorImpl"></bean>

<!-- 2.配置AOP,須要導入AOP名稱空間 -->
<aop:config>
<!-- 聲明切入點表達式 -->
<aop:pointcut expression="execution(* com.neuedu.aop.target.MathCalculatorImpl.*(..))" id="myPointCut"/>
<!-- 配置日誌切面類,引用前面的類 ,經過order屬性控制優先級-->
<aop:aspect ref="logAspect" order="25">
<!-- 經過method屬性指定切面類的切面方法,經過pointcut-ref指定切入點表達式 -->
<aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
<aop:after method="showAfterLog" pointcut-ref="myPointCut"/>
<aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="ex"/>
<aop:after-returning method="showReturnLog" pointcut-ref="myPointCut" returning="result"/>
<aop:around method="around" pointcut-ref="myPointCut"/>
</aop:aspect>

<!-- 配置事務切面類,引用前面的類 -->
<aop:aspect ref="txAspect" order="20">
<aop:around method="around" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>

  

須要知道的是:事務的管理是和AOP是有很大關係的,即聲明式事務的底層是用事務實現的!

附:AOP經過註解實現類的代碼:

 1 //@Component註解將該類添加到IOC容器中,
 2 @Component
 3 //@Aspect註解是切面類的標誌
 4 @Aspect
 5 //@Order註解標註優先級
 6 @Order(value=30)
 7 public class AOPObject {
 8     //環繞通知
 9     @Around(value = "execution(public * com.neuedu.Bean.MathClass.*(..))")
10     public Object show(ProceedingJoinPoint joinPoint){
11         //同前置通知
12         Signature signature = joinPoint.getSignature();
13         Object[] args = joinPoint.getArgs();
14         List<Object> asList = Arrays.asList(args);
15         String name = signature.getName();
16         
17         Object result=null;
18         try{
19             try{
20                 System.out.println(name+"方法開始前,"+asList);
21                 result = joinPoint.proceed(args);
22             }finally{
23                   //同後置通知                               
24                   System.out.println("方法正常結束");
25             }
26             //同返回通知
27             System.out.println("方法最終結束.result爲:"+result.toString());
28         }catch(Throwable ex){
29                         //同異常通知
30             System.out.println("方法異常"+ex.getMessage());
31         }
32         
33         return result;
34     }
35     //重用切面方法
36 /*    @Pointcut(value="execution(public * com.neuedu.Bean.MathClass.*(..))")
37     public void show(){
38         
39     }
40     //前置通知
41     @Before(value= "show()")
42     public void Begin(JoinPoint joinPoint){
43         Object[] args = joinPoint.getArgs();
44         int[] a=new int[args.length];
45         for(int i=0;i<args.length;i++){
46             a[i]=Integer.parseInt(args[i].toString());
47             System.out.println("參數"+i+"爲:"+a[i]);
48         }
49         Signature signature = joinPoint.getSignature();
50         String name = signature.getName();
51         System.out.println(name+"方法開始前,");
52     }
53     //後置通知
54     @After(value= "show()")
55     public void After(){
56         System.out.println("方法正常結束");
57     }
58          //異常通知
59     @AfterThrowing(value= "show()")
60     public void AfterThrowing(){
61         System.out.println("方法異常");
62     }
63         //返回通知
64     @AfterReturning(value= "show()",returning="result")
65     public void AfterReturning(JoinPoint joinPoint, Object result){
66         System.out.println("方法最終結束.result爲:"+result);
67     }*/
68 }        
69         
相關文章
相關標籤/搜索