1. 程式人生 > >【Java入門提高篇】Day11 Java代理——JDK動態代理

【Java入門提高篇】Day11 Java代理——JDK動態代理

創建 pri 建立 ret and etc bool 提高 跳舞

  今天來看看Java的另一種代理方式——JDK動態代理

  我們之前所介紹的代理方式叫靜態代理,也就是靜態的生成代理對象,而動態代理則是在運行時創建代理對象。動態代理有更強大的攔截請求功能,因為可以獲得類的運行時信息,可以根據運行時信息來獲得更為強大的執(騷)行(操)力(作)。

  我們還是以上一個例子為例,這裏的IStars接口和Stars類都不需要修改,只需要修改代理類。

  創建JDK動態代理需要先實現InvocationHandler接口,並重寫其中的invoke方法,具體步驟如下:

  1. 創建一個類實現InvocationHandler接口。

  2. 給Proxy類提供委托類的ClassLoader和Interfaces來創建動態代理類。

  3. 利用反射機制得到動態代理類的構造函數。

  4. 利用動態代理類的構造函數創建動態代理類對象。

  我們用動態代理來改造一下之前的類:

  接口和委托類不需要修改:

public interface IStars {
    void sing();
    void dance();
}

  

public class Stars implements IStars{
    private String name;

    public Stars(String name) {
        this.name = name;
    }

    public String getName() {
        
return name; } public void setName(String name) { this.name = name; } public void sing(){ System.out.println(getName() + " 唱了一首歌."); } public void dance(){ System.out.println(getName() + " 跳了一支舞."); } }

  這是使用動態代理後的代理類:

public class StarsNewProxy implements
InvocationHandler { //代理類持有委托類的對象引用 private Object object; //保存sing和dance的次數 private int num; public StarsNewProxy(Object object){ this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (!runBefore(method)){ return null; }; //利用反射機制將請求分派給委托類處理,Method的invoke返回Object對象作為方法執行結果 Object result = method.invoke(object,args); runAfter(method); return result; } private boolean runBefore(Method method){ System.out.println("我是代理,攔截到請求"); if (method.getName().equals("dance")){ System.out.println("抱歉,明星腳受傷了,不能跳舞表演了。"); return false; } return true; } private void runAfter(Method method){ System.out.println("我是代理,請求處理完畢"); } }

  新建一個工廠類來返回代理實例:

public class StarsNewProxyFactory {
    //構建工廠類,客戶類調用此方法獲得代理對象
    //對於客戶類而言,代理類對象和委托類對象是一樣的,不需要知道具體返回的類型
    public static IStars getInstance(String name){
        IStars stars = new Stars(name);
        InvocationHandler handler = new StarsNewProxy(stars);
        IStars proxy = null;
        proxy = (IStars) Proxy.newProxyInstance(
                stars.getClass().getClassLoader(),
                stars.getClass().getInterfaces(),
                handler
        );
        return proxy;
    }
}

  改寫一下測試類:

public class Test {
    public static void main(String[] args){
//        testA();
        testB();
    }

    /**
     * 靜態代理
     */
    private static void testA(){
        //創建目標對象
        IStars stars = new Stars("Frank");

        //代理對象,把目標傳給代理對象,建立關系
        IStars starsProxy = new StarsProxy(stars);
        for (int i = 0;i < 5; i++){
            starsProxy.sing();
        }
    }

    /**
     * JDK動態代理
     */
    private static void testB(){
        IStars proxy = StarsNewProxyFactory.getInstance("Frank");
        proxy.dance();
        proxy.sing();
    }
}

  輸出如下:

我是代理,攔截到請求
抱歉,明星腳受傷了,不能跳舞表演了。
我是代理,攔截到請求
Frank 唱了一首歌.
我是代理,請求處理完畢

  使用動態代理時實現了InvocationHandler接口並重寫了invoke方法,invoke方法的三個參數:

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

proxy:  被代理的對象
method:  被代理對象的某個方法的Method對象
args:  被代理對象的某個方法接受的參數

  Proxy的newProxyInstance方法詳情如下:

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

loader:  一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行加載

interfaces:  一個Interface對象的數組,表示的是我將要給我需要代理的對象提供一組什麽接口,如果我提供了一組接口給它,那麽這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了

h:  一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上

  可以看到,這裏的動態代理跟靜態代理一樣,在代理類內部保存了一個委托類的實例,實際上都是調用原來的委托實例來進行需要的操作,代理類相當於給委托類加上一個外殼,把委托類置於代理類的內部,從而可以控制客戶類對委托類的訪問,就像上例中,代理類攔截了客戶類對Stars類的dance方法的訪問,並且輸出了補充信息。

  動態代理跟靜態代理最大的不同便是生成代理類的時期不同,靜態代理是在編譯期,而動態代理則是在運行時根據委托類信息動態生成。

  其次,動態代理實現的是InvocationHandler接口,而靜態代理則是直接實現公共接口。當然動態代理也是需要實現相同的接口的,只是將接口信息放在了getInstance內部,相當於代理類跟委托類之間的約定,“這幾個方法幫我代理一下吧”。

  最後,動態代理可以獲得更多的運行時信息,使用起來也會更加靈活。

  至此,JDK動態代理講解完畢,歡迎大家繼續關註!

【Java入門提高篇】Day11 Java代理——JDK動態代理