Java動態代理——InvocationHandler和Proxy詳解

Java 實現動態代理一共有兩種方法,一種是JDK Proxy,另外一種是Cglib,下面就看看JDK動態代理的實現過程,和對實現JDK代理的一個接口InvocationHandler和一個類Proxy的詳解。

首先我們說說靜態代理跟動態代理的概念:

靜態代理:就是由程序員自己創建或由特定工具自動生成源代碼,在程序運行之前,代理類已經存在。(可參考設計模式中的代理模式)。

動態代理:在程序運行時,利用反射機制動態創建而成的。

JDK 代理

要代理的目標對象(委託類)必須實現接口,代理類只能對接口進行代理,要代理的目標對象(委託類)和代理列要實現相同的接口。

下面簡單舉個例子:


//接口
interfa  a {
 void sayHello();
}
//要代理的目標對象(委託類)
class  A implements a{

  public void sayHello(){
  System.out.println("hello,world");
  }
}

要對目標對象(委託類)進行代理 就必須也要實現 a這個接口,但是代理類不是通過直接 implements去實現的,下面會有實現接口a的方式。

在JDK 實現動態代理,我們得先了解一個重要的接口InvocationHandler(實現JDK代理必須實現這個類) 和一個重要的類Proxy

下面先說接口InvocationHandler的作用,看看官方文檔對它的說明

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

翻譯成中文:

InvocationHandler是由代理實例的調用處理程序實現的接口 。

每個代理實例都有一個關聯的調用處理程序。 當在代理實例上調用方法時,方法調用將被編碼並分派到其調用處理程序的invoke方法。

也就是說我們代理類要調用目標對象(委託類)的方法都要經過InvocationHandler這個接口的invoke方法來調用。

看看InvocationHandler中唯一的一個方法invoke

public Object invoke (Object proxy,Method method ,Object [] args) throws Throwable

三個參數的含義:

Object proxy:我們要代理的目標對象

Method method: 代理對象中實現了接口中方法的實例(這裏有點抽象,其實就是代理對象實現了接口中的方法,這個方法也會調用目標對象(委託類)中的同一個方法)

Object [] args: 調用目標對象(委託類)中方法的參數

返回結果:目標對象類中的方法執行後返回的結果

這個方法看着是難以理解,但通過實際操作就比較容易瞭解這個方法的作用了,下面會有更詳細的說明。

再看看Proxy類的官方文檔對它描述

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

翻譯成中文

Proxy提供了創建動態代理類和實例的靜態方法,它也是由這些方法創建的所有動態代理類的超類。

說白了就是這個類提供了爲我們創建代理類的方法,其中最常用的方法就是newProxyInstance()

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException

三個參數的含義:

ClassLoader loader: 類加載器來定義代理類,用那個類加載器來加載代理對象

Class<?>[] interfaces: 代理類要實現的接口列表,就是代理類要實現哪些接口,這裏是個數組,可以實現多個接口

InvocationHandler h:調度方法調用的調用處理函數(調用目標對象的handler)

返回結果:由指定的類加載器定義並實現指定接口的代理類的指定調用處理程序的代理實例

其實就返回我們的代理對象,下面有例子。

上面說了這麼多,直接看個例子更容易理解吧。

接口

public interface A {

    void sayHello();


    int add(int a,int b);
}

目標對象(委託類)

public class AImpl implements A {

    @Override
    public void sayHello() {
        System.out.println("hello , world");
    }

    @Override
    public int add(int a, int b) {
        System.out.println("add()方法的執行結果:" + (a + b));
        return a + b;
    }
}

動態代理類

public class AProxy implements InvocationHandler {
    //我們要代理的目標對象(委託類)
    private AImpl A;

    //通過構造方法將我們的目標對象傳進去
    public AProxy(AImpl a) {
        A = a;
    }
    //處理代理實例上的方法調用並返回結果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在我們真正調用目標對象的方法之前和之後我們可以添加一些自己的操作,例如用戶認證,日誌記錄等
        //例如在我們方法調用之前先進行用戶驗證,調用方法之後再用日誌記錄,下面用僞代碼來說明
        System.out.println("對用戶進行驗證....驗證通過,執行方法");
        //A是我們要進行代理的目標對象,args是執行目標對象中方法所需的參數
        Object invoke = method.invoke(A, args);
        System.out.println("目標對象方法執行後返回的結果:"+invoke);
        System.out.println("日誌:記錄用戶的操作");
        System.out.println();
        //返回的是目標對象方法執行後的結果
        return invoke;
    }
}

客戶類

newProxyInstance()方法返回的動態代理類是JVM運行時動態生成的

public class ProxyTestDemo {

    public static void main(String[] args) {
        //也可以用多態  A a = new AImpl()
        AImpl Aimpl = new AImpl();

        //將我們要代理的目標對象傳進去
        AProxy aProxy = new AProxy(Aimpl);

        //創建目標對象的代理類

        A a = (A) Proxy.newProxyInstance(A.class.getClassLoader(), Aimpl.getClass().getInterfaces(), aProxy);
        
        a.sayHello();

        a.add(1, 2);


    }
}

運行結果

在這裏插入圖片描述

從運行結果可以看到我們在調用目標對象中的方法時,可以前後添加我們的一些操作,其實這跟Spring Aop中Adivce的差不多 ,上面添加的操作類似於前置通知,後置通知。

上面我們還說過InvacationHandler接口中invoke()方法的返回結果是我們目標對象方法執行後的結果,下面證明一下。

直接在動態代理類中添加下面的一句輸出,看看在控制檯中打印出看看是不是我們目標對象方法的運行結果。

在這裏插入圖片描述
控制檯的輸出結果

在這裏插入圖片描述

第一個方法沒有返回值,所以返回null,第二個方法有返回值,所以直接返回目標對象方法的執行結果的返回值,通過上面的輸出可以證明Method的invoke方法是返回目標對象方法的執行結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章