java動(dòng)態(tài)代理類可以分為兩種。
靜態(tài)代理:由程序員創(chuàng)建或特定工具自動(dòng)生成源代碼,再對(duì)其編譯。在程序運(yùn)行前,代理類的.class文件就已經(jīng)存在了。
動(dòng)態(tài)代理:在程序運(yùn)行時(shí),運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成。
一、首先我們進(jìn)行java動(dòng)態(tài)代理的演示。
現(xiàn)在我們有一個(gè)簡(jiǎn)單的業(yè)務(wù)接口Saying,如下:
一個(gè)簡(jiǎn)單的實(shí)現(xiàn)類SayingImpl,如下:
我們要實(shí)現(xiàn)的是,在sayHello和talking之前和之后分別動(dòng)態(tài)植入處理。
JDK動(dòng)態(tài)代理主要用到j(luò)ava.lang.reflect包中的兩個(gè)類:Proxy和InvocationHandler.
InvocationHandler是一個(gè)接口,通過(guò)實(shí)現(xiàn)該接口定義橫切邏輯,并通過(guò)反射機(jī)制調(diào)用目標(biāo)類的代碼,動(dòng)態(tài)的將橫切邏輯和業(yè)務(wù)邏輯編織在一起。
Proxy利用InvocationHandler動(dòng)態(tài)創(chuàng)建一個(gè)符合某一接口的實(shí)例,生成目標(biāo)類的代理對(duì)象。
如下,我們創(chuàng)建一個(gè)InvocationHandler實(shí)例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
MyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//目標(biāo)方法前執(zhí)行
System.out.println("――――――――――――――――――――――――――");
System.out.println("下一位請(qǐng)登臺(tái)發(fā)言!");
//目標(biāo)方法調(diào)用
Object obj = method.invoke(target, args);
//目標(biāo)方法后執(zhí)行
System.out.println("大家掌聲鼓勵(lì)!");
return obj;
}
}
下面是測(cè)試:
運(yùn)行情況如下:
使用JDK動(dòng)態(tài)代理有一個(gè)很大的限制,就是它要求目標(biāo)類必須實(shí)現(xiàn)了對(duì)應(yīng)方法的接口,它只能為接口創(chuàng)建代理實(shí)例。我們?cè)谏衔臏y(cè)試類中的Proxy的newProxyInstance方法中可以看到,該方法第二個(gè)參數(shù)便是目標(biāo)類的接口。如果該類沒(méi)有實(shí)現(xiàn)接口,這就要靠cglib動(dòng)態(tài)代理了。
CGLib采用非常底層的字節(jié)碼技術(shù),可以為一個(gè)類創(chuàng)建一個(gè)子類,并在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用,并順勢(shì)植入橫切邏輯。
二、接下來(lái)我們進(jìn)行cglib動(dòng)態(tài)代理的演示。
首先我們需要導(dǎo)包,我用的包是cglib-nodep-2.1_3.jar。
我們首先創(chuàng)建一個(gè)代理創(chuàng)建器CglibProxy:
public class CglibProxy implements MethodInterceptor{
Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
//設(shè)置需要?jiǎng)?chuàng)建的子類
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通過(guò)字節(jié)碼技術(shù)動(dòng)態(tài)創(chuàng)建子類實(shí)例
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("――――――――――――――――――――――――――");
System.out.println("下一位請(qǐng)登臺(tái)發(fā)言!");
//目標(biāo)方法調(diào)用
Object result = proxy.invokeSuper(obj, args);
//目標(biāo)方法后執(zhí)行
System.out.println("大家掌聲鼓勵(lì)!");
return result;
}
}
然后進(jìn)行測(cè)試:
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
//通過(guò)動(dòng)態(tài)生成子類的方式創(chuàng)建代理類
Saying target = (Saying) proxy.getProxy(SayingImpl.class);
target.sayHello("小明");
target.talking("小麗");
}
}
結(jié)果與JDK動(dòng)態(tài)代理沒(méi)有任何區(qū)別。
JDK動(dòng)態(tài)代理和CGLib動(dòng)態(tài)代理都是運(yùn)行時(shí)增強(qiáng),通過(guò)將橫切代碼植入代理類的方式增強(qiáng)。與此不同的是AspectJ,它能夠在通過(guò)特殊的編譯器在編譯時(shí)期將橫切代碼植入增強(qiáng),這樣的增強(qiáng)處理在運(yùn)行時(shí)候更有優(yōu)勢(shì),因?yàn)镴DK動(dòng)態(tài)代理和CGLib動(dòng)態(tài)代理每次運(yùn)行都需要增強(qiáng)。
新聞熱點(diǎn)
疑難解答
圖片精選