使用JDK動態代理實現裝飾器

使用JDK動態代理實現裝飾器

衆所周知,裝飾器模式和代理模式非常類似,只不過一個是爲了增強功能,一個是訪問控制。

最近在做一個項目,中間遇到了這麼一個需求,在運行時動態的給一個類新增接口。

假設現有的類A是接口 IA 的實現,期望其能夠增加接口 IB 的實現,爲了使用 IB 的方法,首先需要先實現這個方法,可以使用接口默認實現,然後聲明一個匿名類實現,不過爲了簡單起見,創建了一個類 B,其實現了接口 IB。

class Main{
    static interface IA {
        void a();
    }

    static interface IB {
        void b();
    }

    static class A implements IA {

        @Override
        public void a() {
            System.out.println("a");
        }
    }

    static class B implements IB {

        @Override
        public void b() {
            System.out.println("b");
        }
    }

    public static void main(String[] args) {
        A a = new A();
        B b = new B();

        Class<?>[] interfaces = a.getClass().getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println(anInterface);
        }

        Class<?>[] newInterface = new Class[interfaces.length + 1];
        System.arraycopy(interfaces, 0, newInterface, 0, interfaces.length);
        newInterface[interfaces.length] = IB.class;
        Set<Method> collect = Stream.of(IB.class.getMethods()).collect(Collectors.toSet());//獲取接口 IB 的函數列表
        Object o = Proxy.newProxyInstance(a.getClass().getClassLoader(), newInterface, (proxy, method, arg) -> {
            if (collect.contains(method)) {//如果是 IB 的函數調用,則使用b 對象進行實現
                return method.invoke(b, arg);
            } else {
                return method.invoke(a, arg);
            }

        });
        System.out.println(o instanceof IB);
        ((IA) o).a();
        ((IB) o).b();
    }
}

這個方式可以組合兩個完全不相干的接口,因此係統中的非功能型接口,例如打印時希望有一個有意義的名字,名字可以運行時設置,就可以不去都繼承非功能型接口,而是使用裝飾器將他們組合起來,避免因爲繼承導致的體系脆弱以及現有系統的大量修改。

InvocationHandler接口

每一個動態代理類都必須要實現InvocationHandler這個接口,並且每個代理類的實例都關聯到了一個handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由InvocationHandler這個接口的 invoke 方法來進行調用。我們來看看InvocationHandler這個接口的唯一一個方法 invoke 方法:

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

我們看到這個方法一共接受三個參數,那麼這三個參數分別代表什麼呢?

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對象上

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