springAOP

springAOP

前言:
  • 靜態代理
    靜態代理類:
    1、原則:和目標方法類功能一致且和目標類實現同樣的接口。
    2、缺點:

    • 往往在開發我們自己寫的不僅僅是一個業務,兩個業務,而我們的業務會有很多,如果每一個業務開發一個靜態代理類,不僅沒有減少我們的工作量,而且讓我們的工作量多了很多。
    • 如何解決這個問題?能不能爲我們現有的業務層動態的創建代理類,通過動態代理去解決我們現有的業務層中的業務代碼冗餘問題。
  • 動態代理

    通過jdk提供的Proxy類,動態爲現有的業務生成代理類。

代碼示例:

/**
 * JDK中的動態代理
 * @author MOTUI
 *
 */
public class TestUserServiceImplDynamicProxy {
    public static void main(String[] args) {
        final UserService userService = new UserServiceImpl();
        //使用JDK提供的Proxy對象,用來產生動態代理類
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class<?>[] interfaces = {UserService.class};
        InvocationHandler h = new InvocationHandler() {
            //參數1:生成的代理對象
            //參數2:當前調用的方法對象
            //參數3:當前帶哦啊用方法參數
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                try {
                    System.out.println("開啓事務");
                    //調用真正的業務邏輯
                    Object invoke = method.invoke(userService, args);
                    System.out.println("提交事務");
                    return invoke;
                } catch (Exception e) {
                    System.out.println("回滾事務");
                    e.printStackTrace();
                }
                return null;
            }
        };
        //參數1:當前線程的類加載器   加載.class文件
        //參數2:生成代理的對象的目標類的接口類型
        //參數3:當通過代理對象去調方法時,會優先進入InvocationHander類的invoke方法
        UserService userServiceProxy =  (UserService) Proxy.newProxyInstance(loader, interfaces, h);//返回靜態代理對象
        //userServiceProxy.save();
        String query = userServiceProxy.query("小王");
        System.out.println(query);
    }
}

學習AOP需要知道幾個重要的概念:

  • 通知(Advice):處理目標方法以外的操作都稱之爲通知
  • 切入點(PointCut):要爲哪些類中的哪些方法加入通知
  • 切面(Aspect): 通知 + 切入點

    • 前置通知:
      代碼:

      public class RecordMethodNameBeforeAdvice implements MethodBeforeAdvice {
      
          /**
           * 參數1:當前目標類中調用的方法對象
           * 參數2:當前目標類中調用的方法的參數
           * 參數3:目標對象
           */
          @Override
          public void before(Method method, Object[] args, Object target)
                  throws Throwable {
              System.out.println("當前執行的方法:"+method.getName());
          }
      
      }

      配置文件:

          <!-- 管理Bean -->
          <bean id="userService" class="com.motui.first.UserServiceImpl"/>
          <!-- 配置通知 -->
          <bean id="methodBeforeAdvice" class="com.motui.first.RecordMethodNameBeforeAdvice"></bean>
          <!-- 配置切面 -->
          <aop:config>
              <!-- 配置切入點 
              expression:指定哪個類的那個方法需要加入通知-->
              <aop:pointcut expression="execution(* com.motui.first.UserServiceImpl.*(..))" id="pc"/>
              <!-- 組合切入點和通知 -->
              <!-- 
                  advice-ref:告訴當前切面使用的是哪個通知 書寫通知bean的id
                  pointcut-ref:當前通知加入哪個切入點
               -->
              <aop:advisor advice-ref="methodBeforeAdvice" pointcut-ref="pc"/>
          </aop:config>
    • 後置通知:
      代碼:

      public class MyAfter implements AfterReturningAdvice ,ThrowsAdvice {
      //參數1: 目標類中當前執行的方法的返回值
      //參數2: 目標類中當前執行的方法對象
      //參數3: 目標類中當前執行的方法的參數
      //參數4: 目標對象
      @Override
      public void afterReturning(Object result, Method m, Object[] args,
              Object traget) throws Throwable {
          System.out.println("返回值:"+result);
          System.out.println("當前目標類的目標方法"+m.getName());
          System.out.print("參數列表:");
          if (args.length>0) {
              for (Object object : args) {
                  System.out.println(object);
              }
          }else{
              System.out.println("參數爲空");
          }
          System.out.println("目標:"+traget);
      }
      
      //出現異常時進入的通知方法
      public void afterThrowing(Method method, Object[] args, Object target,Exception ex){
          System.out.println("異常通知的方法的名稱: "+method.getName());
          System.out.println("異常通知的方法的參數: "+args);
          System.out.println("異常通知的目標對象"+target);
          System.out.println("異常通知的異常的信息 :" +ex.getMessage());
      }
      }

      配置文件:

      <!-- 管理bean -->             
      <bean id="deptService" class="com.motui.after.DeptServiceImpl"/>
      <bean id="myAfter" class="com.motui.after.MyAfter"/>
      <!-- 配置切入點 -->
      <aop:config>
      <aop:pointcut expression="execution(* com.motui.after.DeptServiceImpl.*(..))" id="pc"/>
      <aop:advisor advice-ref="myAfter" pointcut-ref="pc"/>
      </aop:config>
    • 環繞通知:
      代碼:

      /**
       * 記錄運行時長
       * @author MOTUI
       *
       */
      public class TestAroundAOP implements MethodInterceptor{
      
          @Override
          public Object invoke(MethodInvocation mi) throws Throwable {
              System.out.println("記錄運行時長");
              long start = new Date().getTime();
              Object proceed = mi.proceed();//返回值爲目標方法的返回值,這個返回值在通知類中返回
              long end = new Date().getTime();
              System.out.println("運行時長:"+(end-start));
              return proceed;
          }
      
      }

      配置文件:

          <!-- 管理Service -->
      <bean id="addressService" class="com.motui.around.AddressServiceImpl"/>
      
      <bean id="testAroundAOP" class="com.motui.around.TestAroundAOP"/>
      <!-- 配置切入點 -->
      <aop:config>
          <aop:pointcut expression="execution(* com.motui.around.AddressServiceImpl.*(..))" id="pc"/>
          <aop:advisor advice-ref="testAroundAOP" pointcut-ref="pc"/>
      </aop:config>
    • 異常通知:

      代碼在後置通知中。

    • execution表達式:

      execution(返回值類型 包名.類名.方法名(參數列表))
      示例:
              execution(* com.motui.service.impl.UserServiceImpl.save(..))
                  返回值:任意
                  包:com.motui.service.impl
                  類:UserServiceImpl
                  方法:save
                  參數:任意
      
              execution(* com.motui.service.impl.UserServiceImpl.*(..))
                  返回值:任意
                  包:com.motui.service.impl
                  類:UserServiceImpl
                  方法:任意方法
                  參數:任意
      

在spring中有兩種代理模式(JDK中的proxy和CGLIB):
在spring配置文件中可以通過proxy-target-class屬性指定,true是CGLIB代理;false是Proxy代理

總結:

    Spring的核心思想
    IOC:控制反轉    DI:依賴注入
    AOP:面向切面的編程思想
            AOP:通過動態爲現有項目中的目標類創建代理對象,解決項目中的一些通用問題。
            AOP幾個重要概念:
            通知(Advice):除了目標方法以外的操作稱之爲通知
            切入點(Pointcut):項目中的哪些類的哪些方法需要加入通知
            切面(Aspect):通知 + 切入點
    AOP的底層實現
            1、JDK的動態代理  Proxy   基於接口生成的代理
            2、Spring CGBIL (CGLIB用來生成動態代理) 基於實現類生成的代理
            3、JDK Proxy和CGLIB生成代理的區別?
                Proxy根據接口生成動態代理對象,產生對象的返回值是接口類型
                CGLIB根據實現類生成代理對象,產生對象的返回值是實現類類型(目標類型)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章