java動態代理InvocationHandler和Proxy詳解

今天在整理代理模式時,發現以前對於InvocationHandler中的invoke()方法理解很膚淺,所以重新梳理學習了下.

InvocationHandler接口

InvocationHandler接口是proxy代理實例的調用處理程序實現的一個接口,每一個proxy代理實例都有一個關聯的調用處理程序.
在代理實例調用方法時,方法調用被編碼分派到調用處理程序的invoke()方法.

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

    /**
     * invoke方法
     * @param proxy 代理類代理的真實代理對象com.sun.proxy.$Proxy0
     * @param method 所要調用某個真實對象的方法的Method對象
     * @param args 所要調用某個真實對象的方法的傳遞的參數
     * @return 所要調用某個真實對象的方法的返回值或者返回代理對象
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    }

proxy參數

查了各種資料,個人總結出proxy參數的解釋如下:

  1. 可以使用反射獲取代理對象的信息(也就是proxy.getClass().getName())
  2. 可以將代理對象返回用來進行連續調用.

proxy是真實對象的真實代理對象,invoke方法可以返回調用代理對象方法的返回結果,也可以返回對象的真實代理對象(com.sun.proxy.$Proxy0)

參看以下例子

/**
 * 代理模式中的抽象主題對象
 *
 * @author Jonathan
 * @version 1.0.0
 * @date 2019/8/29 18:14
 * @since 1.0.0+
 */
public interface Subject {

    /**
     * 方法無具體含義,爲了測試參數及返回值類型
     * @param name String
     * @return Subject
     */
    Subject request(String name);

    /**
     * 方法無具體含義,爲了測試方法的返回值
     * @return String
     */
    String getTime();

    /**
     * 方法無具體含義,爲了測試方法無返回值
     */
    void test();

}
/**
 * 具體實現
 *
 * @author Jonathan
 * @version 1.0.0
 * @date 2019/8/29 18:17
 * @since 1.0.0+
 */
public class RealSubject implements Subject {
    /**
     * 方法無具體含義,爲了測試參數及返回值類型
     *
     * @param name String
     * @return Subject
     */
    @Override
    public Subject request(String name) {
        System.out.println("調用request方法");
        return this;
    }

    /**
     * 方法無具體含義,爲了測試方法的返回值
     *
     * @return String
     */
    @Override
    public String getTime() {
        System.out.println("調用getTime方法");
        return new Date().toString();
    }

    /**
     * 方法無具體含義,爲了測試方法無返回值
     */
    @Override
    public void test() {
        System.out.println("調用test方法");
    }
}


/**
 * 代理類的調用
 *
 * @author Jonathan
 * @version 1.0.0
 * @date 2019/8/29 18:19
 * @since 1.0.0+
 */
public class ProxyHandler implements InvocationHandler {

    private Object object;

    public ProxyHandler(Object object) {
        this.object = object;
    }

    /**
     * invoke方法
     * @param proxy 代理類代理的真實代理對象com.sun.proxy.$Proxy0
     * @param method 所要調用某個真實對象的方法的Method對象
     * @param args 所要調用某個真實對象的方法的傳遞的參數
     * @return 所要調用某個真實對象的方法的返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before 動態代理");
        System.out.println("invoke()方法參數一的類型爲:"+proxy.getClass().getName());
        if(method.getName().equals("request")){
            method.invoke(this.object, args);
            System.out.println("after 動態代理");
            return proxy;
        } else if (method.getName().equals("getTime")){
            System.out.println("after 動態代理");
            return method.invoke(this.object, args);
        } else {
            System.out.println("after 動態代理");
            method.invoke(this.object, args);
            return proxy;
        }
    }
}

/**
 * JDK動態代理調用測試
 *
 * @author Jonathan
 * @version 1.0.0
 * @date 2019/8/29 18:25
 * @since 1.0.0+
 */
public class HandlerTest {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        InvocationHandler handler = new ProxyHandler(subject);
        Class<?> clazz = subject.getClass();
        Subject proxy = (Subject) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),handler);
        proxy.request("name").getTime();
        proxy.test();
        System.out.println("代理對象的類型爲:"+ proxy.getClass().getName());
        System.out.println(Proxy.getProxyClass(clazz.getClassLoader(), clazz.getInterfaces()));
        System.out.println(Proxy.isProxyClass(proxy.getClass()));
        System.out.println(Proxy.getInvocationHandler(proxy));
    }
}

輸入結果爲

before 動態代理
invoke()方法參數一的類型爲:com.sun.proxy.Proxy0調requestafterbeforeinvoke():com.sun.proxy.Proxy0 調用request方法 after 動態代理 before 動態代理 invoke()方法參數一的類型爲:com.sun.proxy.Proxy0
after 動態代理
調用getTime方法
before 動態代理
invoke()方法參數一的類型爲:com.sun.proxy.Proxy0after調test:com.sun.proxy.Proxy0 after 動態代理 調用test方法 代理對象的類型爲:com.sun.proxy.Proxy0
class com.sun.proxy.$Proxy0
true
com.clexel.codetree.java.proxy.ProxyHandler@27c170f0

注意代碼中的真實對象中三個方法的返回值(String,返回當前對象,void等).
由以上代碼可以得到結論:

  1. invoke()中的第一個參數運行時的類型是:com.sun.proxy.$Proxy0真實的代理對象
  2. invoke()方法我們既可以返回真實對象方法的返回結果(int,String,等等),也可以將proxy返回,以用來連續調用,參看測試調用代碼中的proxy.request("name").getTime(),其中request()方法的返回結果就是proxy,然後我們可以連續進行調用getTime()方法.
  3. 如果返回this的話,this的類型指的是實現InvocationHandler接口的類(代理類的調用處理程序),並不是代理類$Proxy0

Proxy類說明

Proxy類就是用來創建一個真實對象的真實代理對象,提供了很多方法,最常用的就是newProxyInstance方法

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

方法的作用就是創建一個代理類對象,總共有三個參數,含義如下:

  1. loader:ClassLoader對象,定義了由哪個ClassLoader對象對生成的代理類進行加載.一般指的是真實對象的ClassLoader對象
  2. interfaces:指的是將要給我們的代理對象提供一組什麼樣的接口,也就是聲明代理類實現哪些接口,這樣代理類纔可以調用接口中聲明的方法.一般指的是真實對象所實現的接口.
  3. h: 指的就是實現了InvocationHandler接口的代理類調用處理類,表示的是當動態代理對象調用方法時會關聯到哪個實現了InvocationHandler接口的對象上,並進行轉發到invoke()方法中

Proxy類還有其他的方法:

  1. Proxy.getProxyClass():給定類加載器和接口數組的代理類的java.lang.Class對象。
  2. Proxy.isProxyClass():當且僅當使用getProxyClass方法或newProxyInstance方法將指定的類動態生成爲代理類時,才返回true。
  3. Proxy.getInvocationHandler():返回指定代理實例的調用處理程序
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章