Spring AOP(面向切面編程)

1、AOP簡介

  1.AOP概念:Aspect Oriented Programming 面向切面編程spring

  2.做用:本質上來講是一種簡化代碼的方式數據庫

      繼承機制express

      封裝方法編程

      動態代理ide

      ……測試

  3.情景舉例ui

    ①數學計算器接口[MathCalculator]spa

      int add(int i,int j);.net

      int sub(int i,int j);代理

      int mul(int i, int j);

      int div(int i,int j);

    ②提供簡單實現[EasyImpl]

    ③在簡單實現的基礎上讓每個計算方法都可以打印日誌[LoginImpl]

    ④缺陷

      [1]手動添加日誌繁瑣,重複

      [2]統一修改不便

      [3]對目標方法原本要實現的核心功能有干擾,使程序代碼很臃腫,不易於開發維護

    ⑤使用動態代理實現

      [1]建立一個類,讓這個類可以提供一個目標對象的代理對象

      [2]在代理對象中打印日誌

  4.AOP術語!

    AOP概述

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

      ●Spring的AOP既可使用xml配置的方式實現,也可使用註解的方式來實現!

    4.1          橫切關注點

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

    4.2          切面(Aspect)

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

    4.3          通知(Advice)

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

    4.4          目標(Target)

        被通知的對象

    4.5          代理(Proxy)

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

    4.6          鏈接點(Joinpoint)

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

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

    4.7          切入點(pointcut):

        定位鏈接點的方式。每一個類的方法中都包含多個鏈接點,因此鏈接點是類中客觀存在的事物。

        若是把鏈接點看做數據庫中的記錄,那麼切入點就是查詢條件——AOP能夠經過切入點定位到特定的鏈接點。

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

  5.在Spring中使用AOP實現日誌功能

    ①Spring中可使用註解或XML文件配置的方式實現AOP。

    ②導入jar包

com.springsource.net.sf.cglib -2.2.0.jar
com.springsource.org.aopalliance-1.0.0 .jar
com.springsource.org.aspectj.weaver-1.6.8 .RELEASE.jar
commons-logging-1.1.3. jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE. jar

    ③開啓基於註解的AOP功能

        < aop:aspectj-autoproxy />

    ④聲明一個切面類,並把這個切面類加入到IOC容器中

      @Aspect//表示這是一個切面類

      @Component//加入IOC容器

      public class LogAspect {}

    ⑤在切面類中聲明通知方法

      [1]前置通知:@Before

      [2]返回通知:@AfterReturning

      [3]異常通知:@AfterThrowing

      [4]後置通知:@After

      [5]環繞通知:@Around :環繞通知是前面四個通知的集合體!
        

 1         @Aspect//表示這是一個切面類
 2         @Component//將本類對象加入到IOC容器中!
 3         public class LogAspect {  4           @Before(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))")  5           public void showBeginLog(){  6             System.out.println("AOP日誌開始");  7           }  8           @After(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))")  9           public void showReturnLog(){ 10             System.out.println("AOP方法返回"); 11           } 12           @AfterThrowing(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))") 13           public void showExceptionLog(){ 14             System.out.println("AOP方法異常"); 15           } 16           @AfterReturning(value="execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))") 17           public void showAfterLog(){ 18             System.out.println("AOP方法結束"); 19           } 20         }

    ⑥被代理的對象也須要加入IOC容器

      @Component//加入IOC容器

 1           public class MathCalculatorImpl {  2             public int add(int i,int j){  3               int result = i+j;  4               return result;  5             }  6             public int sub(int i,int j){  7               int result = i-j;  8               return result;  9             } 10             public int multi(int i,int j){ 11               int result = i*j; 12               return result; 13             } 14             public int divide(int i,int j){ 15               int result = i/j; 16               return result; 17             } 18           }

2、切入點表達式:

  1.上述案例經過junit測試,會發現,咱們調用目標類的四個方法只有add方法被加入了4個通知,

    若是想全部的方法都加上這些通知,能夠在切入點表達式處,將execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int)) 換成:

      execution(public int com.neuedu.aop.target.MathCalculatorImpl.*(int, int))這樣只要是有兩個參數,且參數類型爲int的方法在執行的時候都會執行其相應的通知方法!

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

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

      2.1          做用

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

      2.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))

含義

參數類型爲double,double類型的方法

 

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

表達式

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

含義

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


  execution(*  *.*(..))

  1.任意參數,任意類型

  2.任意返回值

  3.用@PointCut註解統一聲明,而後在其它通知中引用該統一聲明便可!

    須要注意的是:權限是不支持寫通配符的,固然你能夠寫一個*表示全部權限全部返回值!

  4.最詳細的切入點表達式:
    execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))
  5.最模糊的切入點表達式:
    execution (* *.*(..))

  6..統一聲明切入點表達式
    @Pointcut(value= "execution(public int com.atguigu.aop.target.EazyImpl.add(int,int))")
    public void myPointCut(){}

  三、通知方法的細節

    ①在通知中獲取目標方法的方法名和參數列表

      [1]在通知方法中聲明一個JoinPoint類型的形參

      [2]調用JoinPoint對象的getSignature()方法獲取目標方法的簽名

      [3]調用JoinPoint對象的getArgs()方法獲取目標方法的實際參數列表

    ②在返回通知中獲取方法的返回值

      [1]在@AfterReturning註解中添加returning屬性

        @AfterReturning (value="myPointCut()", returning= "result")

      [2]在返回通知的通知方法中聲明一個形參,形參名和returning屬性的值一致

        showReturnLog(JoinPoint joinPoint, Object result)

    ③在異常通知中獲取異常對象

      [1]在@ AfterThrowing註解中添加throwing屬性

        @AfterThrowing (value="myPointCut()",throwing= "throwable" )

      [2]在異常通知的通知方法中聲明一個形參,形參名和throwing屬性值一致

        showExceptinLog(JoinPoint joinPoint, Throwable throwable)

  根據接口類型獲取target對象時,實際上真正放在IOC容器中的對象是代理對象,而並非目標對象自己!

  四、.環繞通知:@Around

    4.1.環繞通知須要在方法的參數中指定JoinPoint的子接口類型ProceedingJoinPoint爲參數

      @Around(value="pointCut()")

      public void around(ProceedingJoinPoint joinPoint){

      }
    4.2.環繞通知會將其餘4個通知能幹的,本身都給幹了!

      注意:@Around修飾的方法必定要將方法的返回值返回!自己至關於代理!

 1         @Around(value="pointCut()")  2         public Object around(ProceedingJoinPoint joinPoint){  3           Object[] args = joinPoint.getArgs();  4           Signature signature = joinPoint.getSignature();  5           String methodName = signature.getName();  6           List<Object> list = Arrays.asList(args);  7           Object result = null;  8           try {  9             //目標方法以前要執行的操做
10             System.out.println("[環繞日誌]"+methodName+"開始了,參數爲:"+list); 11             //調用目標方法
12             result = joinPoint.proceed(args); 13             //目標方法正常執行以後的操做
14             System.out.println("[環繞日誌]"+methodName+"返回了,返回值爲:"+result); 15           } catch (Throwable e) { 16 
17             //目標方法拋出異常信息以後的操做
18             System.out.println("[環繞日誌]"+methodName+"出異常了,異常對象爲:"+e); 19             throw new RuntimeException(e.getMessage()); 20           }finally{ 21             //方法最終結束時執行的操做!
22             System.out.println("[環繞日誌]"+methodName+"結束了!"); 23           } 24           return result; 25         }

3、切面的優先級

    對於同一個代理對象,可j以同時有多個切面共同對它進行代理。

    能夠在切面類上經過@Order (value=50)註解來進行設置,值越小優先級越高!

 1    @Aspect  2    @Component  3    @Order(value=40)  4    public class TxAspect {  5 
 6     @Around(value="execution(public * com.neuedu.aop.target.MathCalculatorImpl.*(..))")  7     public Object around(ProceedingJoinPoint joinPoint){  8       Object[] args = joinPoint.getArgs();  9       Signature signature = joinPoint.getSignature(); 10       String methodName = signature.getName(); 11       List<Object> list = Arrays.asList(args); 12       Object result = null; 13       try { 14         //目標方法以前要執行的操做
15         System.out.println("[事務日誌]"+methodName+"開始了,參數爲:"+list); 16         //調用目標方法
17         result = joinPoint.proceed(args); 18 
19         //目標方法正常執行以後的操做
20         System.out.println("[事務日誌]"+methodName+"返回了,返回值爲:"+result); 21       } catch (Throwable e) { 22         //目標方法拋出異常信息以後的操做
23         System.out.println("[事務日誌]"+methodName+"出異常了,異常對象爲:"+e); 24         throw new RuntimeException(e.getMessage()); 25       }finally{ 26         //方法最終結束時執行的操做!
27         System.out.println("[事務日誌]"+methodName+"結束了!"); 28       } 29       return result; 30     } 31 
32   }

4、經過配置方式實現AOP

  一、注意:上面的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是有很大關係的,即聲明式事務的底層是用事務實現的!

相關文章
相關標籤/搜索