天天看點

JDK動态代理深入探究

在上一篇文章設計模式之代理模式裡,我們說了JDK動态代理類,不過我相信好多人都有疑惑,包括我,剛開始學的時候都是一臉懵逼,那麼到底疑惑在哪裡呢?

我的疑惑就是這個InvocationHandler的invoke方法到底是有啥用?我們都沒有調用它。newProxyInstance傳回的東西到底是啥?等等,這篇文章我們就一起來探讨一下吧。

首先我們先寫一個簡單的動态代理吧,有例子更好說明。還是買車的例子,哈哈,說明我挺想買車的。我就直接複制上篇文章的。

// Car.java
package com.codeliu.dao;

public interface Car {
    // vip客戶可以直接買車
    public void buyCar();
}      
// CarImp1.java
package com.codeliu.dao.impl;
import com.codeliu.dao.Car;
/**
 * 委托類1
 */
public class CarImp1 implements Car {
    public void buyCar() {
        System.out.println("我是vip1");
    }
}      
// CarProxy.java
package com.codeliu.dao.impl;

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

/**
 * 動态代理類
 */
public class CarProxy implements InvocationHandler {
    private Object target;
    /**
     * 綁定一個委托對象并獲得一個代理類對象
     * @param target [description]
     * @return [description]
     */
    public Object bind(Object target) {
        this.target = target;
        // 取得代理對象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), 
             this);
    }
    @Override
    //這個方法并不是我們自己去調用
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable {
        System.out.println("調用這個方法前:" + method);
        // 執行委托類的方法
        Object result = method.invoke(target,args);
        System.out.println("調用這個方法後:" + method);
        return result;
    }
}      
package com.codeliu.test;

import com.codeliu.dao.impl.CarImp1;
import com.codeliu.dao.impl.CarImp2;
import com.codeliu.dao.impl.CarProxy;
import com.codeliu.dao.Car;
public class TestProxy {
    public static void main(String[] args) {
        CarProxy cp = new CarProxy();
        // 傳入一個實作了該接口的執行個體就行
        Car car = (Car)cp.bind(new CarImp1());
        // Car car = (Car)cp.bind(new CarImp2());
        car.buyCar();
    }
}      

好了,那麼現在我們就來看看Test.java裡生成的car對象到底是個什麼東a西,是我們的Car接口嗎?(我一開始一直了解不通)

我們利用反射可以擷取到car對應類的大部分資訊,好了,現在我們把Test.java改成如下形式

package com.codeliu.test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.codeliu.dao.Car;
import com.codeliu.dao.impl.CarImp1;
import com.codeliu.dao.impl.CarProxy;

public class Test {
    public static void main(String[] args) {
        CarProxy cp = new CarProxy();
        // 傳入一個實作了該接口的執行個體就行
        Car car = (Car)cp.bind(new CarImp1());
        // Car car = (Car)cp.bind(new CarImp2());
        car.buyCar();

        System.out.println("car是Proxy類的子類嗎?" + (car instanceof Proxy));
        Class<?> c = car.getClass();
        System.out.println(c);
        System.out.println("car的Class類是:" + c.getName());
        Field[] fields = c.getDeclaredFields();
        System.out.print("car的Class類中有哪些字段:");
        for(Field f:fields) {
            System.out.print(f.getName() + " ");
        }
        System.out.println();
        System.out.print("car的Class類中有哪些方法:");
        Method[] methods = c.getDeclaredMethods();
        for(Method m:methods) {
            System.out.print(m.getName() + " ");
        }
        System.out.println();
        System.out.println("car的Class類的父類是:" + c.getSuperclass());

        Class<?>[] class1 = c.getInterfaces();
        System.out.print("car的Class類實作的接口有:");
        for(Class<?> c1:class1) {
            System.out.println(c1.getName() + " ");
        }
    }
}      

運作一下,輸出如下

調用這個方法前:public abstract void com.codeliu.dao.Car.buyCar()
我是vip1
調用這個方法後:public abstract void com.codeliu.dao.Car.buyCar()
car是Proxy類的子類嗎?true
class com.sun.proxy.$Proxy0
car的Class類是:com.sun.proxy.$Proxy0
car的Class類中有哪些字段:m1 m2 m3 m0 
car的Class類中有哪些方法:equals toString hashCode buyCar 
car的Class類的父類是:class java.lang.reflect.Proxy
car的Class類實作的接口有:com.codeliu.dao.Car      

恩,發現了什麼,這個玩意竟然是一個Proxy類的子類,它的名字是$Proxy0,實作了我們自己定義的Car接口,還有四個字段和方法。

既然知道了上面的結果,那麼我們就去看看newProxyInstance方法的源碼,應該能找到答案了。

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
 throws IllegalArgumentException{
     Objects.requireNonNull(h);
     /*
      * Look up or generate the designated proxy class.
      */
     Class<?> cl = getProxyClass0(loader, intfs);
}      

我就截了比較重要的,getProxyClass0方法,它的作用就是生成一個proxy類,好,我們看看這個方法。

private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
}      

再去看看這個get方法,emmmmm,代碼太多了,尼瑪,我也看不太懂,不過經過我查資料,總結如下

newProxyInstance方法調用getProxyClass0生成一個$Proxy0類。

恩沒了,現在我們要找答案就得去看看$Proxy0類的源碼了。

因為生成的是一個.class檔案,是以我們先反編譯後,可以看到源碼如下

package com.sun.proxy;

import com.codeliu.dao.Car;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Car {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler paramInvocationHandler) {
       super(paramInvocationHandler);
    }

    public final boolean equals(Object paramObject) {
        try {
            return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        } catch (Error|RuntimeException localError {
              throw localError;
        } catch (Throwable localThrowable) {
              throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final String toString() {
        try {
            return (String)this.h.invoke(this, m2, null);
        } catch (Error|RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final void buyCar() {
        try {
            this.h.invoke(this, m3, null);
            return;
        } catch (Error|RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final int hashCode() {
       try {
           return ((Integer)this.h.invoke(this, m0, null)).intValue();
       } catch (Error|RuntimeException localError) {
           throw localError;
       } catch (Throwable localThrowable) {
           throw new UndeclaredThrowableException(localThrowable);
       }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.codeliu.dao.Car").getMethod("buyCar", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
    }
}      

$Proxy0繼承了Proxy類并實作了Car接口,它有一個構造方法,傳入了一個InvocationHandler執行個體并調用了父類的構造方法,然後是四個final方法,其他三個是Object類傳過來的,還有一個叫buyCar,這不就是我們在接口中定義的嗎?我們看看它怎麼寫的

public final void buyCar() {
    try {
        this.h.invoke(this, m3, null);
        return;
    } catch (Error|RuntimeException localError) {
        throw localError;
    } catch (Throwable localThrowable) {
        throw new UndeclaredThrowableException(localThrowable);
    }
}      

恩,首先m3在靜态塊中,取得Car接口中的buyCar方法,然後是調用invoke方法,666,現在終于解決了文章開頭的疑惑,原來在這裡調用了,等等,那麼這個h又是什麼東西,我們看看Proxy的源碼

protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    this.h = h;
}      
public $Proxy0(InvocationHandler paramInvocationHandler) {
    super(paramInvocationHandler);
}      

繼續閱讀