概念介紹
靜態(tài)代理
由程序員主動(dòng)創(chuàng)建或由特定工具自動(dòng)生成源代碼,再對(duì)其編譯炮叶。在程序運(yùn)行前,代理類的.class文件就已經(jīng)存在了渡处。
動(dòng)態(tài)代理
代理類在程序運(yùn)行前并不存在镜悉,在程序運(yùn)行時(shí)動(dòng)態(tài)生成(無需手工編寫代理類源碼)
實(shí)現(xiàn)原理:Java反射機(jī)制
實(shí)現(xiàn)原理
Java編譯器編譯好Java文件之后,產(chǎn)生.class 文件在磁盤中医瘫。這種class文件是二進(jìn)制文件侣肄,內(nèi)容是只有JVM虛擬機(jī)能夠識(shí)別的機(jī)器碼。JVM虛擬機(jī)讀取字節(jié)碼文件醇份,取出二進(jìn)制數(shù)據(jù)稼锅,加載到內(nèi)存中吼具,解析.class 文件內(nèi)的信息,生成對(duì)應(yīng)的 Class對(duì)象:
由于JVM通過字節(jié)碼的二進(jìn)制信息加載類的矩距,那么拗盒,如果我們?cè)谶\(yùn)行期系統(tǒng)中,遵循Java編譯系統(tǒng)組織.class文件的格式和結(jié)構(gòu)锥债,生成相應(yīng)的二進(jìn)制數(shù)據(jù)陡蝇,然后再把這個(gè)二進(jìn)制數(shù)據(jù)加載轉(zhuǎn)換成對(duì)應(yīng)的類,這樣哮肚,就完成了在代碼中登夫,動(dòng)態(tài)創(chuàng)建一個(gè)類的能力了。
具體實(shí)現(xiàn)
- 接口TargetInterface
public interface TargetInterface {
public int targetMethodA(int number);
public int targetMethodB(int number);
}
- 具體實(shí)現(xiàn)類ConcreteClass
public class ConcreteClass implements TargetInterface {
@Override
public int targetMethodA(int number) {
System.out.println("開始調(diào)用目標(biāo)類的方法targetMethodA...");
System.out.println("操作-打印數(shù)字:"+number);
System.out.println("結(jié)束調(diào)用目標(biāo)類的方法targetMethodA...");
return number;
}
@Override
public int targetMethodB(int number) {
System.out.println("開始調(diào)用目標(biāo)類的方法targetMethodB...");
System.out.println("操作-打印數(shù)字:"+number);
System.out.println("結(jié)束調(diào)用目標(biāo)類的方法targetMethodB...");
return number;
}
}
- 動(dòng)態(tài)代理類ProxyHandler
調(diào)用代理類目標(biāo)接口方法時(shí)允趟,對(duì)自動(dòng)將其轉(zhuǎn)發(fā)到代理處理器中的invoke()方法中恼策,invoke()方法內(nèi)部可以實(shí)現(xiàn)預(yù)處理,對(duì)委托類方法調(diào)用潮剪,后續(xù)處理等邏輯戏蔑。
public class ProxyHandler implements InvocationHandler {
private Object concreteClass;
public ProxyHandler(Object concreteClass){
this.concreteClass=concreteClass;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy:"+proxy.getClass().getName());
System.out.println("method:"+method.getName());
System.out.println("args:"+args[0].getClass().getName());
System.out.println("Before invoke method...");
Object object=method.invoke(concreteClass, args);//普通的Java反射代碼,通過反射執(zhí)行某個(gè)類的某方法
//System.out.println(((ConcreteClass)concreteClass).targetMethod(10)+(Integer)args[0]);
System.out.println("After invoke method...");
return object;
}
}
- 測(cè)試類DynamicProxyExample
public class DynamicProxyExample {
public static void main(String[] args){
ConcreteClass c=new ConcreteClass();//元對(duì)象(被代理對(duì)象)
InvocationHandler ih=new ProxyHandler(c);//代理實(shí)例的調(diào)用處理程序。
//創(chuàng)建一個(gè)實(shí)現(xiàn)業(yè)務(wù)接口的代理類,用于訪問業(yè)務(wù)類(見代理模式)鲁纠。
//返回一個(gè)指定接口的代理類實(shí)例总棵,該接口可以將方法調(diào)用指派到指定的調(diào)用處理程序,如ProxyHandler改含。
TargetInterface targetInterface=
(TargetInterface)Proxy.newProxyInstance(c.getClass().getClassLoader(),c.getClass().getInterfaces(),ih);
//調(diào)用代理類方法,Java執(zhí)行InvocationHandler接口的方法.
int i=targetInterface.targetMethodA(5);
System.out.println(i);
System.out.println();
int j=targetInterface.targetMethodB(15);
System.out.println(j);
}
}
優(yōu)點(diǎn)
- 減少編程的工作量:假如需要實(shí)現(xiàn)多種代理處理邏輯情龄,只要寫多個(gè)代理處理器就可以了,無需每種方式都寫一個(gè)代理類捍壤。
- 系統(tǒng)擴(kuò)展性和維護(hù)性增強(qiáng)骤视,程序修改起來也方便多了(一般只要改代理處理器類就行了)。
不足
目前根據(jù)GOF的代理模式鹃觉,代理類和委托類需要都實(shí)現(xiàn)同一個(gè)接口(在代理類實(shí)例化時(shí)需傳入目標(biāo)接口)专酗。也就是說只有實(shí)現(xiàn)了某個(gè)接口的類可以使用Java動(dòng)態(tài)代理機(jī)制。但是盗扇,事實(shí)上使用中并不是遇到的所有類都會(huì)給你實(shí)現(xiàn)一個(gè)接口祷肯。因此,對(duì)于沒有實(shí)現(xiàn)接口的類疗隶,目前無法使用該機(jī)制佑笋。
參考資料
[1]Java動(dòng)態(tài)代理機(jī)制詳解(JDK 和CGLIB,Javassist斑鼻,ASM)
[2]Java動(dòng)態(tài)代理詳解