軟件開發(fā)的最終目的是以不變應(yīng)萬變,為此我們的前輩們總結(jié)了很多模式來應(yīng)對各種需求,代理模式就是其中的一種。假如我們已經(jīng)理解了靜態(tài)代理,那動態(tài)代理就相對容易理解了,被代理的類是動態(tài)變化的,是一個泛型的 target; 那到底怎么實現(xiàn)的呢。
Java的動態(tài)代理,會用到InvocationHandler、Proxy 。代碼設(shè)計的時候考慮到方便擴展和維護,都需要抽象出公共特性,例如共有的方法。 動態(tài)代理的出現(xiàn)是為了解決:
1.控制外部調(diào)用
2.增強某些方法的處理
3.需要在N多類的同一個方法執(zhí)行前或者執(zhí)行后做一些事情(AOP)
代碼例子:
/**
* 公共接口,抽象出公共方法
*/
public interface Person {
public void giveTask();
}
/**
* 具體的人,例如學(xué)生,實現(xiàn)交作業(yè)的方法
*/
public class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
public void task() {
System.out.println(name+"交作業(yè)");
}
}
/**
* 具體的人,老師改作業(yè)
*/
public class Tescher implements Person {
@Override
public void task() {
System.out.println("老師改作業(yè)");
}
}
如果不用代理,那直接就調(diào)用Student和Teacher類了,那我們現(xiàn)在想在所有的task方法執(zhí)行前都干點事情,也許你說可以用抽象類,然后抽象類中實現(xiàn)方法進行處理,也不是不行,但是這樣帶來問題,我將來想擴展更多的類是不是需要改動父類或者我super調(diào)用不及時,等等這樣的問題。 所以動態(tài)代理的出現(xiàn)就是為了不侵入的方式去增強原有的方法。
/**
* 我想在原有的老師或者學(xué)生做事情前,干點啥又不用去改動原來的方法
* 這就是一個Person代理方法處理,并不是代理類哦,代理是通過他動態(tài)創(chuàng)建的
* 在代理類里調(diào)用本類的invoke方法,從而調(diào)用目標類的方法
* @param <T> 泛型
*/
public class PersonInvocationHandler<T> implements InvocationHandler {
// 被代理的對象
T target;
public PersonInvocationHandler(T target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理執(zhí)行" + method.getName() +"方法");
// 用反射執(zhí)行被代理對象的方法
Object ret = method.invoke(target,args);
return ret;
}
}
具體調(diào)用:
public static void main(String[] args) {
// 學(xué)生
Person xiaoming = new Student("小明");
// 老師
Person laoshi = new Tescher();
// 構(gòu)建代理方法處理者,就是通過他要在別人方法前后干點事情
PersonInvocationHandler invocationHandler1 = new PersonInvocationHandler(laoshi);
// 通過Proxy.newProxyInstance 生成一個新的代理類對象,這個方法硬生生的創(chuàng)造出了一個新的類出來$Proxy0
// 并且按照定義的接口,實現(xiàn)了接口中的方法,方法內(nèi)部是調(diào)用 invocationHandler的invoke()
Person person1 = (Person)Proxy.newProxyInstance(Tescher.class.getClassLoader(),new Class<?>[]{Person.class},invocationHandler1);
// 表面上看是調(diào)用Person.task(), 實際上是調(diào)用新創(chuàng)建的代理類的task()方法,神不知鬼不覺。
person1.task();
}
運行結(jié)果如你猜想的一樣:
代理執(zhí)行task方法
老師改作業(yè)
動態(tài)代理之所以動態(tài),就在于用泛型接收被代理對象和完全動態(tài)的創(chuàng)建了一個新的代理類出來。 怎么知道創(chuàng)建了新的類呢,在Proxy.newProxyInstance前設(shè)置一下參數(shù),就會輸出動態(tài)創(chuàng)建的類到項目目錄下,$Proxy0.class這個就是剛剛創(chuàng)建被編譯成字節(jié)碼的文件。
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
利用IDEA反編譯查看一下
/*
繼承Proxy,方便調(diào)用invocationHandler,還記得Proxy.newProxyInstance傳進去的,實現(xiàn)Person接口
*/
public final class $Proxy0 extends Proxy implements Person {
//以下是提取的 方法
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
// 其他部分省略了,這里是獲取方法,利用反射找到被代理的方法
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.alienjun.dynamictest.Person").getMethod("task");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
// 根據(jù)接口實現(xiàn)task()方法,這里只是調(diào)用父類的invocationHandler.invoke(),具體執(zhí)行task是在invocationHandler.invoke中執(zhí)行的
public final void task() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
到這里就已經(jīng)明白整個動態(tài)代理過程了,
其中怎么創(chuàng)建的類,又是怎么被編譯和加載調(diào)用的更多細節(jié)可以細看源碼。