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