1. 程式人生 > >AOP的基礎知識總結(jdk動態代理/cglib動態代理)

AOP的基礎知識總結(jdk動態代理/cglib動態代理)

AOP的基礎知識總結(jdk動態代理/cglib動態代理)

知識總結

aop切面程式設計

切面:

  切面包含了通知和切點,通知和切點共同定義了切面是什麼,在何時,何處執行切面邏輯。

切點:

  如果說通知定義了在何時執行通知,那麼切點就定義了在何處執行通知。所以切點的作用就是
      通過匹配規則查詢合適的連線點(Joinpoint),AOP 會在這些連線點上織入通知。

通知:

   Spring 中對應了 5 種不同型別的通知:
      · 前置通知(Before):在目標方法執行前,執行通知
      · 後置通知(After):在目標方法執行後,執行通知,此時不關係目標方法返回的結果是什麼
      · 返回通知(After-returning):在目標方法執行後,執行通知
      · 異常通知(After-throwing):在目標方法丟擲異常後執行通知
      · 環繞通知(Around): 目標方法被通知包裹,通知在目標方法執行前和執行後都被會呼叫

動態代理:

Spring在選擇用JDK還是CGLiB的依據:

  (1)當Bean實現介面時,Spring就會用JDK的動態代理(就比如userServiceImpl實現了IUserService)
  (2)當Bean沒有實現介面時,Spring使用CGlib是實現(就比如userServiceImpl沒有實現IUserService)
  (3)可以強制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)

CGlib比JDK快?

(1)使用CGLib實現動態代理,CGLib底層採用ASM位元組碼生成框架,使用位元組碼技術生成代理類,比使用Java反射效率要高。唯一需要注意的是,CGLib不能對宣告為final的方法進行代理,因為CGLib原理是動態生成被代理類的子類。
(2)在對JDK動態代理與CGlib動態代理的程式碼實驗中看,1W次執行下,JDK7及8的動態代理效能比CGlib要好20%左右。

JDK動態代理和CGLIB位元組碼生成的區別?

 (1)JDK動態代理只能對實現了介面的類生成代理,而不能針對類
 (2)CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法
   因為是繼承,所以該類或方法最好不要宣告成final

而cglib動態代理是利用asm開源包,對代理物件類的class檔案載入進來,通過修改其位元組碼生成子類來處理。

  1、如果目標物件實現了介面,預設情況下會採用JDK的動態代理實現AOP 
  2、如果目標物件實現了介面,可以強制使用CGLIB實現AOP 
  3、如果目標物件沒有實現了介面,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換

Spring的aop的基本的使用:

增加依賴包:

  jdk:不需要額外新增依賴
  cglib:
    org.springframework.spring-aop.5.0.8.RELEASE
    org.aspectj.aspectjweaver.1.8.13
    org.springframework.spring-aspects.5.0.8.RELEASE
    aopalliance.aopalliance.1.0

增加切面類類:

  ① 在類上使用 @Component 註解 把切面類加入到IOC容器中
  ② 在類上使用 @Aspect 註解 使之成為切面類

案例(可以只使用環繞通知/其他的前置後置通知之一):

  @Component
  @Aspect
  public class LoggingAspect {

      /**
       * 前置通知:目標方法執行之前執行以下方法體的內容
       * @param jp
       */
      @Before("execution(* com.qcc.beans.aop.*.*(..))")
      public void beforeMethod(JoinPoint jp){
          String methodName = jp.getSignature().getName();
          System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
      }

      /**
       * 返回通知:目標方法正常執行完畢時執行以下程式碼
       * @param jp
       * @param result
       */
      @AfterReturning(value="execution(* com.qcc.beans.aop.*.*(..))",returning="result")
      public void afterReturningMethod(JoinPoint jp, Object result){
          String methodName = jp.getSignature().getName();
          System.out.println("【返回通知】the method 【" + methodName + "】 ends with 【" + result + "】");
      }

      /**
       * 後置通知:目標方法執行之後執行以下方法體的內容,不管是否發生異常。
       * @param jp
       */
      @After("execution(* com.qcc.beans.aop.*.*(..))")
      public void afterMethod(JoinPoint jp){
          System.out.println("【後置通知】this is a afterMethod advice...");
      }

      /**
       * 異常通知:目標方法發生異常的時候執行以下程式碼
       */
      @AfterThrowing(value="execution(* com.qcc.beans.aop.*.*(..))",throwing="e")
      public void afterThorwingMethod(JoinPoint jp, NullPointerException e){
          String methodName = jp.getSignature().getName();
          System.out.println("【異常通知】the method 【" + methodName + "】 occurs exception: " + e);
      }

  //  /**
  //   * 環繞通知:目標方法執行前後分別執行一些程式碼,發生異常的時候執行另外一些程式碼
  //   * @return
  //   */
  //  @Around(value="execution(* com.qcc.beans.aop.*.*(..))")
  //  public Object aroundMethod(ProceedingJoinPoint jp){
  //      String methodName = jp.getSignature().getName();
  //      Object result = null;
  //      try {
  //          System.out.println("【環繞通知中的--->前置通知】:the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
  //          //執行目標方法
  //          result = jp.proceed();
  //          System.out.println("【環繞通知中的--->返回通知】:the method 【" + methodName + "】 ends with " + result);
  //      } catch (Throwable e) {
  //          System.out.println("【環繞通知中的--->異常通知】:the method 【" + methodName + "】 occurs exception " + e);
  //      }
  //
  //      System.out.println("【環繞通知中的--->後置通知】:-----------------end.----------------------");
  //      return result;
  //  }
  }