SpringAOP-動態代理,日誌注入

SpringAOP

前言:

1.AOP定義? 用來幹啥的? 怎麼用?(怎麼跑通它的思路)

代理模式

為啥要學代理模式? — 因為是SpringAop的底層

原有的程式碼不敢動,一動就是Bug,。所以使用代理可以做很多事,

  • 在原有的基礎上,進行封裝一層,客戶使用的就我們這一層封裝後的功能

代理模式的分類:

  • 靜態代理

  • 角色分析:

    • 抽象角色:一般用介面或者抽象類來解決,
    • 真實角色:被代理的角色
    • 代理角色:代理真實角色,代理真實角色後,我們一般做一些附屬操作
    • 客戶:訪問代理角色的人
  • 代理模式的好處:1.可以使真實角色的操作更加純粹!不用去關注一些公共的業務

    • 2.公共的業務交給了代理角色–實現了業務的分工
    • 3.業務發送擴展的時候,方便集中管理
  • 缺點:

    • 一個真實角色就會產生一個代理角色,程式碼量翻倍—開發效率bian低
  • 操作步驟—1,介面–>真實角色–》代理角色–》客戶端訪問代理

  • 靜態代理這麼麻煩,每次重寫人家的很多方法???—怎麼解決程式碼多—動態代理

  • 個人理解代理:無非是在原有的基礎上,進行一層封裝,這樣既能使用被代理人的方法,也能實現自我的靈活安排!

  • 動態代理

    • 動態代理和靜態代理角色一樣
    • 動態代理類是動態生成的,不是我們直接寫好的,,,想想直接將被代理人的很多方法都再次複述
    • 動態代理分了兩大類—1.基於介面的動態代理—2.基於類的動態代理
      • 基於介面—JDK動態代理 — 注意,,,基於介面的!!!
      • 基於類:cglib
      • java位元組碼:javasist
    • 需要了解兩個類:Proxy,代理–InvocationHandler 調用處理程式 —-使用時:
      • Proxy提供了創建動態代理類和實例的靜態方法,它也是由這些方法創建的所有動態代理類的超類。
      • InvocationHandler介面提供的-newProxyInstance返回指定介面的代理類的實例,該介面將方法調用分派給指定的調用處理程式。
        Proxy.newProxyInstance因為與IlegalArgumentException相同的原因而Proxy.getProxyClass.
  • 它兩結合返回的proxyInstance—Proxy.newProxyInstance

  • 至於被代理人方法,使用invoke操作

  • 目的:生成一個代理對象,並執行被代理人方法

  • 交互–被代理人方法,以及生成代理對象,提供一個調用代理人對象的方法

  • 1.被代理人的方法的獲取,必須要獲取到被代理人的對象

  • 2.生成代理對象,必須要依託於代理人的方法(介面)以及它的載入器(要不jvm處理不了)

  • 建立一個動態代理

    • 目的:生成一個代理對象,並執行被代理人方法

    • 交互–被代理人方法,以及生成代理對象,提供一個調用代理人對象的方法

      • 1.被代理人的方法的獲取,必須要獲取到被代理人的對象
      • 2.生成代理對象,必須要依託於代理人的方法(介面)以及它的載入器(要不jvm處理不了)
            package com.zjz.ProxyMethod;
            
            /*
            * 目的:生成一個代理對象,並執行被代理人方法
            * 交互--被代理人方法,以及生成代理對象,提供一個調用代理人對象的方法
            * 1.被代理人的方法的獲取,必須要獲取到被代理人的對象
            * 2.生成代理對象,必須要依託於代理人的方法(介面)以及它的載入器(要不jvm處理不了)
            *
            * */
      
            
            import java.lang.reflect.InvocationHandler;
            import java.lang.reflect.Method;
            import java.lang.reflect.Proxy;
            
            public class ProxyInvocationHandler implements InvocationHandler {
            
                private Object target; // 被代理的人-對象
            
                public void setTarget(Object target) {
                    this.target = target;
                }
            
            
                // 調用代理人對象的方法,以及生成代理對象
                public Object getProxy(){
            
                    Object proxyInstance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
                    return proxyInstance;
                }
            
                // 被代理人方法
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("執行"+method.getName()+"方法---");
                    Object result = method.invoke(target, args); // 此時執行被代理人的方法
            
                    return result;
                }
            }
      
      
      
      
  • 關於代理中為啥自動運行invoke,而且不可缺少

  • Proxy.newProxyInstance 執行時會生成一個創建出一個類$Proxy0,此類中調用invoke

  • 如果少了還會執行,但是咱們的代理對象沒有方法了。。
    Object result = method.invoke(被代理對象的介面, args); // 執行 被代理對象的介面方法,,invoke

AOP

每走一步,對照代理模式

介紹

  • AOP(Aspect Oriented Programming)意為:面向切面編程,通過預編譯方式和運行期動態代理實現
    程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring框架中的
    一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使
    得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

Aop在Spring中的作用

提供聲明式事務;允許用戶自定義切面

  • 橫切關注點:跨越應用程式多個模組的方法或功能。即是,與我們業務邏輯無關的,
    但是我們需要關注的部分,就是橫切關注點。如日誌 , 安全 , 快取 , 事務等等 ….
  • 切面(ASPECT):橫切關注點 被模組化 的特殊對象。即,它是一個類。
  • 通知(Advice):切面必須要完成的工作。即,它是類中的一個方法。
  • 目標(Target):被通知對象。
  • 代理(Proxy):向目標對象應用通知之後創建的對象。
  • 切入點(PointCut):切面通知 執行的 「地點」的定義。
  • 連接點(JointPoint):與切入點匹配的執行點。

  • SpringAOP中,通過Advice定義橫切邏輯,Spring中支援5種類型的Advice:

使用Spring實現Aop

  • 1.導包

  • 【重點】使用AOP織入,需要導入一個依賴包!

    <!-- //mvnrepository.com/artifact/org.aspectj/aspectjweaver --> 
      <dependency> 
        <groupId>org.aspectj</groupId> 
        <artifactId>aspectjweaver</artifactId>
       <version>1.9.4</version> 
     </dependency>
    
    
  • 2.目的

    • 1.切入的配置
      • 切入的位置
        • 注意:execution(* com.zjz.serivce.UserServiceImpl.*(..))
          • 第一個表示所有…第二個切入的類,然後.之後應該是方法(..)表示所有方法..
      • 切入方法的加入
    • 2.切入的編寫
      • 三種方法…

使用Spring的API介面

目的:將日誌,或者其它玩意,,切入到要執行的介面

使用代理模式:怎麼用? 你依據API寫好你要切的東西,然後交給ApplicationContext去做(定位!配置你寫的)

為啥交給間接管理員,服務者。。。你要在大的生態里整東西就是靠它。。

關鍵:ApplicationContext怎麼做,,,,要切的東西怎麼寫

  • 要切的東西怎麼寫;

    • 1.要實現org.springframework.aop.下的介面 —對應的Xml中要加對應的資源
    • 2.直接使用它的方法(like-a) 擴展點就好了
  • 配置:關鍵!!

    • 1.約束,資源導入 aop的
    • 2.aop配置 <aop:config>

      • 1.切入的位置 aop:pointcut
      • 2.將我們的東西配進去 aop:advisor
      <!--方式1,使用Spring API介面-->
          <!--配置AOP,需要導入AOP的約束-->
          <aop:config>
      <!--切入點 expression:表達式 execution(要執行的位置!* * * * *)-->
              <aop:pointcut id="pointcut" expression="execution(* com.zjz.serivce.UserServiceImpl.*(..))"/>
      
      <!-- 執行環繞增強!-->
          <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
      
          <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
          </aop:config>
      
    
    

自定義類來實現Aop

目的:將日誌,或者其它玩意,,切入到要執行的介面

使用代理模式:怎麼用? 你寫好你要切的東西(自己編的),然後交給ApplicationContext去做(定位!配置你寫的)

為啥交給間接管理員,服務者。。。你要在大的生態里整東西就是靠它。。

關鍵:ApplicationContext怎麼做,,,,要切的東西怎麼寫

  • 要切的東西怎麼寫;

    • 1.自己寫一個類,裡面自己定義幾個方法—
        public class DiyPointCut {
           public void before(){
               System.out.println("=======方法執行前=======");
           }
           public void after(){
               System.out.println("=======方法執行後=======");
           }
       }
    
  • 配置

    • 1.約束,資源導入 aop的

    • 2.我們自己編寫bean導入,

    • 3.aop配置 <aop:config>

      • 導入我們的配置,讓我們的寫的東西進行賦予方法<aop:aspect ref="diy">
        • 1.切入的位置 aop:pointcut
        • 2.將我們的東西配進去 aop:before aop:after
      
          <!--方式2:自定義類-->
              <bean id="diy" class="com.zjz.diy.DiyPointCut"/>
                  <aop:config>
                      <aop:aspect ref="diy">
          <!--切入點-->
                      <aop:pointcut id="point" expression="execution(* com.zjz.serivce.UserServiceImpl.*(..))"/>
          <!--通知-->
                          <aop:before method="before" pointcut-ref="point"/>
                          <aop:after method="after" pointcut-ref="point"/>        
                      </aop:aspect>
                  </aop:config>
          
      
      
      

註解實現Aop

  • 1.自己寫一個加註解的類

        // 使用註解方式實現AOP
        @Aspect  // 標註這個類是一個切面
        public class AnnotationPointCut {
        
            @Before("execution(* com.zjz.serivce.UserServiceImpl.*(..))")
            public void before(){
                System.out.println("======方法執行前=====");
            }
        
            @After("execution(* com.zjz.serivce.UserServiceImpl.*(..))")
            public void after(){
                System.out.println("=====方法執行後=====");
            }
        
            // 在環繞增強中,我們可以給定一個參數,代表我們要處理切入的點
            @Around("execution(* com.zjz.serivce.UserServiceImpl.*(..))")
            public void around(ProceedingJoinPoint pj) throws Throwable {
                System.out.println("環繞前---");
        
                // 獲得簽名
                Signature signature = pj.getSignature();
                System.out.println(signature);
        
                // 執行方法
                Object proceed = pj.proceed();
        
                System.out.println(proceed);
                System.out.println("環繞後---");
        
            }
    
    
  • 配置

  • 1.約束,資源導入 aop的

  • 2.我們自己編寫bean導入,

  • 3.開啟註解支援


  <!--方式3 註解配置-->
      <bean id="annotationPointCut" class="com.zjz.diy.AnnotationPointCut"/>
  <!--開啟註解支援-->
      <aop:aspectj-autoproxy/>