1. 程式人生 > 其它 >動態代理學習小結

動態代理學習小結

技術標籤:java反射動態代理

動態代理

1.代理模式

1.1什麼是代理模式

  • 代理模式是指,為其他物件提供一種代理以控制對這個物件的訪問。在某些情況下,
    一個物件不適合或者不能直接引用另一個物件,而代理物件可以在客戶類和目標物件之
    間起到中介的作用。

  • 例如: 有 A, B(代理類), C (目標類)三個類, A 原來可以呼叫 C 類的方法, 現在因為某種原因 C 類不允許A 類呼叫其方法,但 B 類可以呼叫 C 類的方法。 A 類通過 B 類呼叫 C 類的方法。這裡 B 是 C 的代理。 A 通過代理 B 訪問 C。
    在這裡插入圖片描述
    在這裡插入圖片描述

1.2代理模式作用

  • 功能增強: 在原有的功能上,增加了額外的功能。 新增加的功能,叫做功能增強。
  • 控制訪問: 代理類不讓你直接訪問目標,例如中介不讓租戶直接訪問房東。

1.3代理模式分類

  • 靜態代理

  • 動態代理

2.靜態代理

2.1什麼是靜態代理

  • 靜態代理是指,代理類在程式執行前就已經定義好.java 原始檔,其與目標類的關係在
    程式執行前就已經確立。 在程式執行前代理類已經編譯為.class 檔案。
  • (1)代理類是自己手工實現的,自己手動建立一個java類,表示代理類。
    (2)同時你所要代理的目標類是確定的。
    特點: 1)實現簡單 2)容易理解。

2.2靜態代理缺點

  • 當一個專案中,目標類和代理類很多時候,有以下的缺點:

(1)程式碼複雜,難於管理

代理類和目標類實現了相同的介面, 每個代理都需要實現目標類的方法, 這樣就出現了大量的代

碼重複。如果介面增加一個方法,除了所有目標類需要實現這個方法外,所有代理類也需要實現
此方法。增加了程式碼維護的複雜度 ,耦合度也高。

(2)代理類依賴目標類,代理類過多

代理類只服務於一種型別的目標類,如果要服務多個型別。勢必要為每一種目標類都進行代理,
靜態代理在程式規模稍大時就無法勝任了,代理類數量過多。

3.動態代理

3.1什麼是動態代理

  • 動態代理是指代理類物件在程式執行時由JVM 根據反射機制動態生成的。 動態代理不
    需要定義代理類的.java 原始檔。
    動態代理其實就是 jdk 執行期間, 動態建立 class 位元組碼並載入到 JVM。

3.2動態代理的實現方式

  • jdk動態代理(理解): 使用java反射包中的類和介面實現動態代理的功能。是基於介面的代理類


    反射包 java.lang.reflect , 裡面有三個類 : InvocationHandler , Method, Proxy。

  • cglib動態代理(瞭解): cglib是第三方的工具庫, 建立代理物件。

    cglib的原理是繼承,cglib通過繼承目標類,建立它的子類,在子類中重寫父類中同名的方法, 實現功能的修改。因為cglib是繼承,重寫方法,所以要求目標類不能是final的, 方法也不能是final的。cglib的要求目標類比較寬鬆, 只要能繼承就可以了。cglib在很多的框架中使用, 比如 mybatis ,spring框架中都有使用。

3.3jdk動態代理需要用到的方法和類

  • (1) InvocationHandler 介面

    通 過 代 理 對 象 執 行 目 標 接 口 中 的 方 法 , 會 把 方 法 的 調 用 分 派 給 調 用 處 理 器
    (InvocationHandler)的實現類,執行實現類中的 invoke()方法,我們需要把功能代理寫在 invoke()方法中

    public interface InvocationHandler {//代理類必須實現這個介面,並重寫invoke()方法
         /**
         * 該方法在目標類的方法被執行的時候,會被呼叫
         * 呼叫目標類方法時,會先執行該invoke方法
         * @param proxy 真正的代理物件,即Proxy.newProxyInstance()方法的返回結果
         * @param method 目標類中的方法,jdk自動幫我們提供物件
         * @param args 目標類中方法的引數(可變引數),也是jdk自動幫我們的提供的
         * @return null
         * @throws Throwable
         */
         public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
        
    }
    
  • (2) Method 類

    表示目標類中的方法

    InvocationHandler實現類的invoke()方法的第二個引數為 Method 類物件,該類有一個方法也叫 invoke(),可以呼叫目標方法。這兩個 invoke()方法,雖然同名,但無關

     public Object invoke(Object obj, Object... args){}
     //obj:表示目標物件
     //args:表示目標方法引數,就是InvocationHandler實現類的invoke方法的第三個引數(可變引數)
    

    該方法的作用是:呼叫執行 obj 物件所屬類的方法,這個方法由其呼叫者 Method 物件確定。
    在程式碼中,一般的寫法為
    method.invoke(target, args);
    其中, method 為上一層 invoke 方法的第二個引數。這樣,即可呼叫目標類的目標方法。

  • ( 3) Proxy 類

    通 過 JDK 的 java.lang.reflect.Proxy 類 實 現 動 態 代 理 , 使 用 其 靜 態 方 法newProxyInstance(),依據目標物件、業務介面及呼叫處理器三者,自動生成一個動態代理物件。

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h) {}
    //loader:目標類的類載入器,通過目標物件的反射可獲取
    //interfaces:目標類實現的介面陣列,通過目標物件的反射可獲取
    //handler: 呼叫處理器。
    

3.4 jdk 動態代理實現

​ jdk 動態代理是代理模式的一種實現方式,其只能代理介面。實現步驟:
​ 1、新建一個介面,作為目標介面
​ 2、為介面建立一個實現類,是目標類
​ 3、建立類實現 java.lang.reflect.InvocationHandler 介面,呼叫目標方法並增加其他功能程式碼
​ 4、 建立動態代理物件,使用 Proxy.newProxyInstance()方法,並把返回值強制轉為介面型別。

3.5jdk動態代理的例子

  • 目標介面
public interface TargetClass { 
    void sayHello();
}
  • 目標介面的實現類

    public class TargetClassImpl implements TargetClass{    
        @Override    
        public void sayHello() {        
            System.out.println("Hi,dynamic proxy");
        }
    }
    
  • 建立類實現 java.lang.reflect.InvocationHandler 介面

    /**
     * ProxyClass是動態代理的一部分,還不是真正的代理類,該類是協助真正的代理類去工作
     */
    public class ProxyClass implements InvocationHandler {
        private Object target;//這裡要傳入一個目標類
    
        public ProxyClass(Object target) {
            this.target = target;
        }
    
        /**
         * 該方法在目標類的方法被執行的時候,會被呼叫
         * 呼叫目標類方法時,會先執行該invoke方法
         * @param proxy 真正的代理物件,即Proxy.newProxyInstance()方法的返回結果
         * @param method 目標類中的方法,jdk自動幫我們提供物件
         * @param args 目標類中方法的引數(可變引數),也是jdk自動幫我們的提供的
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("目標方法執行前。。。。。。");
    
            //此程式碼是真正的目標方法sayHello()
            Object result = method.invoke(target,args);
            
    		//這些增加的方法表示增強功能作用
            System.out.println("目標方法執行後。。。。。。");
            return result;
        }
    }
    
  • 建立動態代理物件來呼叫目標類

    public class Client {
        public static void main(String[] args) {
    
            //使用代理模式
            //Proxy.newProxyInstance()這個方法的返回結果才是我們的真正的代理物件
            TargetClass targetClass = (TargetClass)Proxy.newProxyInstance(TargetClass.class.getClassLoader(),
                                    new Class<?>[] {TargetClass.class},
                                    new ProxyClass(new TargetClassImpl()));
            targetClass.sayHello();
        }
    }