[Java5新特性] 動態代理

作者信息
作者姓名:金雲龍
個人網站:http://www.longestory.com
個人公衆帳號:搜索“longestory”或“龍哥有話說

動態代理概述

代理模式是Java設計模式中的一種,其特徵爲代理類與委託類有同樣的接口,代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後處理消息等。代理類與委託類之間通常存在關聯關係,一個代理類的對象與一個委託類的對象關聯,代理類的對象本身並不真正實現業務,而是通過調用委託類對象的相關方法來提供具體業務。

在Java中的java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口,通過這個類和接口可以生成JDK動態代理或動態代理對象。

按照代理的創建時間不同,可以分爲兩種:

  • 靜態代理:手動創建,再對其編譯。在程序運行前,代理類的.class文件就已經存在。
  • 動態代理:在程序運行時,通過反射機制動態創建而成。

動態代理原理

動態代理的實現原理有些類似於過濾器的實現原理,但有所不同。動態代理的代理類與委託類之間的關係更像是明星與經紀人之間的關係,也就是說,如果你想找某個明星演出的話,並不是找他本人,而是找到他的經紀人就可以了。動態代理的實現過程很類似於這個過程,具體請看下圖:

這裏寫圖片描述

Proxy代理類

Proxy類是Java的java.lang.reflect包下提供的,該類用於創建動態代理類和代理對象的靜態方法,它也是所有動態代理類的父類。如果在程序中爲一個或多個接口動態地生成實現類,就可以用Proxy類來創建動態代理類;如果需要爲一個或多個接口動態地創建實例,也可以使用Proxy類來創建動態代理實例。

方法摘要
static InvocationHandler getInvocationHandler(Object proxy)
static Class<?> getProxyClass(ClassLoader loader, Class<?>… interfaces)
static boolean isProxyClass(Class<?> cl)
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
  • static Class<?> getProxyClass(ClassLoader loader, Class<?>… interfaces):創建一個動態代理類所對應的Class對象,該代理類將實現interfaces所指定的多個接口。第一個ClassLoader參數指定生成動態代理類的類加載器。
  • static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):直接創建一個動態代理對象,該代理對象的實現類實現了interfaces指定的系列接口,執行代理對象的每個方法時都會被替換執行InvocationHandler對象的invoke()方法。

InvocationHandler

InvocationHandler接口提供了invoke()方法,用於替換代理對象的每一個方法。真實業務類可以通過代理類對象調用InvocationHandler接口提供的invoke()方法,來替代調用委託類的真實方法。

以下是InvocationHandler的API內容:

方法摘要
Object invoke(Object proxy, Method method, Object[] args)

Object invoke(Object proxy, Method method, Object[] args):在代理實例上處理方法調用並返回結果。在與方法關聯的代理實例上調用方法時,將在調用處理程序上調用此方法。

  • 參數proxy:表示代理類對象,也就是Proxy.newProxyInstance()方法返回的對象,通常用不上。
  • 參數method:表示當前被調用方法的反射對象。
  • 參數args:表示調用目標方法時傳入的實參。

實現動態代理

利用Java提供的Proxy類和InvocationHandler接口來生成動態代理類或動態代理對象,具體實現步驟如下:

  • 定義一個業務接口,該接口提供具體業務方法的定義。
public interface Person {
    void sayMe();
    void sayHello(String name);
}
  • 定義一個InvocationHandler接口的實現類,並重寫invoke()方法。
public class MyInvocationHandler implements InvocationHandler {
    /**
     * 執行動態代理對象的所有方法時,都會被替換成執行下面的invoke()方法.
     *  * 參數proxy:代表動態代理對象.
     *  * 參數method:代表正在執行的方法.
     *  * 參數args:代表調用目標方法時傳入的實參.
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("---正在執行的方法: "+method);
        if(args == null){
            System.out.println("當前調用的方法沒有參數.");
        }else{
            System.out.println("當前調用的方法需要傳入的實參爲:");
            for (Object val : args) {
                System.out.println(val);
            }
        }
        return null;
    }
}
  • 編寫一個用於測試動態代理的測試類。
public class ProxyTest {
    public static void main(String[] args) {
        // 創建一個InvocationHandler對象
        InvocationHandler handler = new MyInvocationHandler();
        // 通過Proxy類使用指定的InvocationHandler來生成動態代理對象
        Person p = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, handler);
        // 調用動態代理對象的業務方法
        p.sayMe();
        p.sayHello("張無忌");
    }
}

動態代理的作用

通過Java提供的Proxy類和InvocationHandler接口生成的動態代理類,可以阻止調用委託類的方法、過濾參數及修改對應方法的返回值等作用。實現業務接口方法的實現類即委託類,具體操作如下:

  • 創建一個實現類,實現Person接口,並重寫業務方法。
public class Fanbingbing implements Person {
    @Override
    public void sayMe() {
        System.out.println("我真的是范冰冰哦!");
    }
    @Override
    public String sayHello(String name) {
        System.out.println("你好:"+name+",我等你很久了...");
        return "我終於見到范冰冰啦!";
    }
}
  • 編寫一個用於測試動態代理的測試類。
public class FanbingbingTest {
    public static void main(String[] args) {
        Person p = (Person) Proxy.newProxyInstance(
                Person.class.getClassLoader(),
                Fanbingbing.class.getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                        // 通過method的getName()方法獲取業務方法名,進行阻止.
                        if (method.getName().equals("sayMe")) {
                            System.out.println("你想多了,哪那麼容易見到啊!");
                            return null;
                        }
                        // 通過args獲取實參,進行修改
                        if(method.getName().equals("sayHello")){
                            String name = (String)args[0];
                            method.invoke(Class.forName("app.java.proxy.Fanbingbing").newInstance(), "某局長");
                        }
                        // 修改返回值
                        if(method.getName().equals("sayHello")){
                            return "都是假的!";
                        }
                        return null;
                    }
                });
        p.sayMe();
        p.sayHello("張無忌");
    }
}

轉載說明:請註明作者及原文鏈接,謝謝!

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