玩轉JDK動態代理

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/chyohn/article/details/54983293

概述

JDK動態代理的兩個主要類:
java.lang.reflect.InvocationHandler:執行代理對象的方法時的回調接口。在這個接口中編寫與代理業務相關的代碼。它在JDK中的定義如下。

public interface InvocationHandler {
    /**
     * @param proxy JDK生成的代理對象
     * @param method 被代理接口的方法對象
     * @param args 方法執行的參數
     * @return  
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

java.lang.reflect.Proxy:調用它的靜態方法newProxyInstance創建代理對象。它爲代理的接口創建實現類,併爲其創建一個以InvocationHandler對象爲參數的構造方法,每個代理方法都會執行InvocationHandler對象的invoke方法。

運用

A. 魚兒開始和停止游泳時告訴大家

通過jdk動態代理,可以在一個行爲前後添加性能監控、日誌等操作。

下面我們來記錄魚兒開始和停止游泳的日誌。

(1)定義一個FishService接口和實現

public interface FishService{
    /**
    * 游泳
    **/
    void swimming();
}

實現如下

public class FishServiceImpl implements FishService {
    public void swimming() {
        System.out.println("游泳中....");
    }
}

(2)定義SimpleInvocationHandler 用於獲得代理對象和和方法回調。
在這裏該類的定義稍微複雜了點,直接看我的註釋就是。


public class SimpleInvocationHandler implements InvocationHandler {

    private Set<Class> interfaces = new HashSet<>();
    // 接口和默認實現
    private Map<Class, Object> additionalItf = new HashMap<>();

    /**
     * 添加被代理接口和其實現
     * @param itf 被代理的接口
     * @param defaultImpl 接口的默認實現
     * @param <T>
     */
    public <T> void additionalItf(Class<T> itf, T defaultImpl) {
        additionalItf.put(itf, defaultImpl);
        interfaces.add(itf);
    }

    /**
     * 獲得代理對象
     * 通過Proxy的靜態方法newProxyInstance創建代理
     * @return 返回代理對象
     */
    public Object getProxy() {
        ClassLoader loader = SimpleInvocationHandler.class.getClassLoader();
        Class[] interfaces = this.interfaces.toArray(new Class[0]);
        return Proxy.newProxyInstance(loader, interfaces, this);
    }

    /**
     * 實現InvocationHandler接口的invoke方法。
     * 該方法在執行代理對象的方法時,會回調執行。
     * @return 返回被代理的方法的返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        String name = method.getName();
        try {
            System.out.println(name + "方法執行前");
            // 根據方法的聲明類對象,獲取方法的調用者
            Object defaultImpl = additionalItf.get(method.getDeclaringClass());
            if (defaultImpl == null) {
                System.out.println(name + "方法無默認實現");
                return null;
            }
            return method.invoke(defaultImpl, args);
        } finally {
            System.out.println(name + "方法執行後");
        }

    }
}

(3)創建代理,並執行接口方法。

public class MainObject {
    public static void main(String[] args) throws Exception {

        SimpleInvocationHandler handler = new SimpleInvocationHandler();
        // 添加被代理接口和實現類
        handler.additionalItf(FishService.class, new FishServiceImpl());

        // 獲得代理對象
        FishService fishService = (FishService) handler.getProxy();
        // 通過代理對象執行代理方法
        fishService.swimming();
    }

}

輸入如下。

swimming方法執行前
游泳中....
swimming方法執行後

B. 讓魚兒飛起來

可以通過JDK動態代理多個接口,使對象提供更多的服務。操作如下。

(1)定義一個飛行的接口和一個默認實現。

interface FlyService {
    /**
     * 飛行
     */
    void fly();
}

實現飛行接口,如下。

public class FlyServiceImpl implements FlyService {
    @Override
    public void fly() {
        System.out.println("飛...");
    }
}

(2)通過SimpleInvocationHandler 創建一個會飛的魚,代碼如下。


class MainObject {
    public static void main(String[] args) throws Exception {

        SimpleInvocationHandler handler = new SimpleInvocationHandler();
        handler.additionalItf(FishService.class, new FishServiceImpl());
        handler.additionalItf(FlyService.class, new FlyServiceImpl());

        FishService fishService = (FishService) handler.getProxy();
        fishService.swimming();
        // 讓魚飛起來
        ((FlyService)fishService).fly();
    }

}

運行結果:

swimming方法執行前
游泳中....
swimming方法執行後
fly方法執行前
飛...
fly方法執行後

總結

JDK動態代理的作用:
(1)通過JDK代理可以爲接口創建代理。
(2)通過JDK代理可以爲類增加更多的行爲。

JDK動態代理的不足:
只能爲接口創建代理,而不能爲一個class類創建代理。

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