JDK動態代理與Proxy類和InvocationHandler接口分析

原文鏈接:https://blog.csdn.net/yaomingyang/article/details/80981004

java動態代理機制中有兩個重要的類和接口InvocationHandler(接口)和Proxy(類),這一個類Proxy和接口InvocationHandler是我們實現動態代理的核心;

1.InvocationHandler接口是proxy代理實例的調用處理程序實現的一個接口,每一個proxy代理實例都有一個關聯的調用處理程序;在代理實例調用方法時,方法調用被編碼分派到調用處理程序的invoke方法。
看下官方文檔對InvocationHandler接口的描述:

     {@code InvocationHandler} is the interface implemented by
     the <i>invocation handler</i> of a proxy instance.

     <p>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 {@code invoke}
     method of its invocation handler.

每一個動態代理類的調用處理程序都必須實現InvocationHandler接口,並且每個代理類的實例都關聯到了實現該接口的動態代理類調用處理程序中,當我們通過動態代理對象調用一個方法時候,這個方法的調用就會被轉發到實現InvocationHandler接口類的invoke方法來調用,看如下invoke方法:

    /**
    * proxy:代理類代理的真實代理對象com.sun.proxy.$Proxy0
    * method:我們所要調用某個對象真實的方法的Method對象
    * args:指代代理對象方法傳遞的參數
    */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

2.Proxy類就是用來創建一個代理對象的類,它提供了很多方法,但是我們最常用的是newProxyInstance方法。

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

     Returns an instance of a proxy class for the specified interfaces
     that dispatches method invocations to the specified invocation
     handler.  This method is equivalent to:

這個方法的作用就是創建一個代理類對象,它接收三個參數,我們來看下幾個參數的含義:

loader:一個classloader對象,定義了由哪個classloader對象對生成的代理類進行加載
interfaces:一個interface對象數組,表示我們將要給我們的代理對象提供一組什麼樣的接口,如果我們提供了這樣一個接口對象數組,那麼也就是聲明瞭代理類實現了這些接口,代理類就可以調用接口中聲明的所有方法。
h:一個InvocationHandler對象,表示的是當動態代理對象調用方法的時候會關聯到哪一個InvocationHandler對象上,並最終由其調用。
3動態代理中核心的兩個接口和類上面已經介紹完了,接下來我們就用實例來講解下具體的用法
首先我們定義一個接口People

package reflect;

public interface People {

    public String work();
}

定義一個Teacher類,實現People接口,這個類是真實的對象

package reflect;

public class Teacher implements People{

    @Override
    public String work() {
        System.out.println("老師教書育人...");
        return "教書";
    }
    }

現在我們要定義一個代理類的調用處理程序,每個代理類的調用處理程序都必須實現InvocationHandler接口,代理類如下:

package reflect;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class WorkHandler implements InvocationHandler{

    //代理類中的真實對象  
    private Object obj;

    public WorkHandler() {
        // TODO Auto-generated constructor stub
    }
    //構造函數,給我們的真實對象賦值
    public WorkHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在真實的對象執行之前我們可以添加自己的操作
        System.out.println("before invoke。。。");
        Object invoke = method.invoke(obj, args);
        //在真實的對象執行之後我們可以添加自己的操作
        System.out.println("after invoke。。。");
        return invoke;
    }

}

上面的代理類的調用處理程序的invoke方法中的第一個參數proxy好像我們從來沒有用過,而且關於這個參數的具體用法含義請參考我的另外一篇文章Java中InvocationHandler接口中第一個參數proxy詳解

接下來我們看下客戶端類

package reflect;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Test {

    public static void main(String[] args) {
        //要代理的真實對象
        People people = new Teacher();
        //代理對象的調用處理程序,我們將要代理的真實對象傳入代理對象的調用處理的構造函數中,最終代理對象的調用處理程序會調用真實對象的方法
        InvocationHandler handler = new WorkHandler(people);
        /**
         * 通過Proxy類的newProxyInstance方法創建代理對象,我們來看下方法中的參數
         * 第一個參數:people.getClass().getClassLoader(),使用handler對象的classloader對象來加載我們的代理對象
         * 第二個參數:people.getClass().getInterfaces(),這裏爲代理類提供的接口是真實對象實現的接口,這樣代理對象就能像真實對象一樣調用接口中的所有方法
         * 第三個參數:handler,我們將代理對象關聯到上面的InvocationHandler對象上
         */
        People proxy = (People)Proxy.newProxyInstance(handler.getClass().getClassLoader(), people.getClass().getInterfaces(), handler);
        //System.out.println(proxy.toString());
        System.out.println(proxy.work());
    }

看下輸出結果:

before invoke。。。
老師教書育人...
after invoke。。。
教書

通過上面的講解和示例動態代理的原理及使用方法,在Spring中的兩大核心IOC和AOP中的AOP(面向切面編程)的思想就是動態代理,在代理類的前面和後面加上不同的切面組成面向切面編程。

上面我們只講解了Proxy中的newProxyInstance(生成代理類的方法),但是它還有其它的幾個方法,我們下面就介紹一下:

getInvocationHandler:返回指定代理實例的調用處理程序
getProxyClass:給定類加載器和接口數組的代理類的java.lang.Class對象。
isProxyClass:當且僅當使用getProxyClass方法或newProxyInstance方法將指定的類動態生成爲代理類時,才返回true。
newProxyInstance:返回指定接口的代理類的實例,該接口將方法調用分派給指定的調用處理程序

發佈了27 篇原創文章 · 獲贊 3 · 訪問量 6385
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章