java動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理在工作中用代理的地方非常多,但一直還沒(méi)仔細(xì)來(lái)看代理的原理,今天被同事提到,所以自己開(kāi)始仔細(xì)研究了一下這兩者代理都做了些什么工作,并通過(guò)編寫(xiě)測(cè)試用例的方式來(lái)對(duì)兩種代理原理作理解。
在自行看代碼之前,初步問(wèn)了一下朋友,大概解釋這兩者區(qū)別是,java動(dòng)態(tài)代理是利用反射機(jī)制生成一個(gè)實(shí)現(xiàn)代理接口的匿名類(lèi),在調(diào)用具體方法前調(diào)用InvokeHandler來(lái)處理。而cglib動(dòng)態(tài)代理是利用asm開(kāi)源包,對(duì)代理對(duì)象類(lèi)的class文件加載進(jìn)來(lái),通過(guò)修改其字節(jié)碼生成子類(lèi)來(lái)處理。這是朋友說(shuō)的,我并沒(méi)自己實(shí)驗(yàn)過(guò),所以也沒(méi)映象,所以開(kāi)始自己動(dòng)手實(shí)踐之:
java動(dòng)態(tài)代理
使用方法:
接口:
public interface Call {
void doCall(String doCall);
}
public interface Processor {
void doProcess(String doProcess);
}
實(shí)現(xiàn)類(lèi):
public class ServiceImpl implements Call, Processor {
public void doCall(String doCall) {
System.out.println("doCall");
}
public void doProcess(String doProcess) {
System.out.println("doProcess");
}
}
具體代理Handler:
public class ServiceHandler implements InvocationHandler {
private Call callService;
public ServiceHandler(Call callService) {
this.callService = callService;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("proxyMethod=" + method.getName());
Object obj = method.invoke(this.callService, args);
System.out.println("after invoke!");
return obj;
}
}
使用java動(dòng)態(tài)代理:
public class JdkProxyTest {
@Test
public void testJdkProxy() {
Call call = new ServiceImpl();
ServiceHandler handler = new ServiceHandler(call);
Call callProxy = (Call) Proxy.newProxyInstance(call.getClass().getClassLoader(),
new Class[]{Call.class}, handler);
callProxy.doCall("test");
}
}
最終效果就是執(zhí)行代理接口的doCall方法之前,該方法被ServiceHandler給處理了。
通過(guò)查看java.lang.reflect.Proxy代碼,大致擬了一下它的實(shí)現(xiàn)原理:
1. 取到new Class[]{Call.class}這里所有接口,通過(guò)Class.forName把接口類(lèi)加載到JVM,放到內(nèi)部Set里保存,把接口的完善名字保存,帶包名的接口名字,并以把這組接口名稱(chēng)數(shù)組轉(zhuǎn)換成List作為key,用于下面生成代理類(lèi)后保存到內(nèi)部Map的key.也就是相當(dāng)于這一組的接口名稱(chēng)對(duì)應(yīng)的一個(gè)生成的代理類(lèi)
2. 主要是從內(nèi)存里找是否之前已經(jīng)生成好了這同一組接口的代理類(lèi),如果有就直接拿出。這里第一次是需要新建立的,所以開(kāi)始創(chuàng)建代理,首先檢查代理目標(biāo)接口的訪(fǎng)問(wèn)控制符是否是默認(rèn)包級(jí)別的,如果是就需要給生成的代理類(lèi)設(shè)置目標(biāo)接口同樣的包名,才能默認(rèn)訪(fǎng)問(wèn)這種級(jí)別下的接口。如果這種有默認(rèn)訪(fǎng)問(wèn)控制標(biāo)識(shí)符的目標(biāo)接口,又有不同包名的目標(biāo)接口,則會(huì)報(bào)出錯(cuò)誤。否則其它情況,是給的無(wú)包名的代理類(lèi),生成的代理類(lèi)的默認(rèn)名稱(chēng)是$Proxy開(kāi)頭加Proxy里標(biāo)識(shí)唯一類(lèi)名的數(shù)字,是靜態(tài)long型變量,每次生成一次代理類(lèi)會(huì)累加
3. 調(diào)用ProxyGenerator.generateProxyClass(proxyName, interfaces)動(dòng)態(tài)生成class字節(jié)碼類(lèi),該類(lèi)相當(dāng)于是Proxy的子類(lèi),實(shí)現(xiàn)了需要代理的接口方法,并在每個(gè)方法里調(diào)用了InvocationHandler的invoke方法,而我們自己實(shí)現(xiàn)的InvocationHandler接口類(lèi)里完成了以反射方式最終對(duì)目標(biāo)業(yè)務(wù)類(lèi)的接口方法進(jìn)行調(diào)用。所以此種方式實(shí)現(xiàn)的動(dòng)態(tài)代理只能代理接口方法,對(duì)具體類(lèi)的代理不能實(shí)現(xiàn)。
http://hi.baidu.com/dobug/blog/item/493f817e802479340cd7dab9.html