java動態代理類可以分為兩種。
靜態代理:由程序員創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。
動態代理:在程序運行時,運用反射機制動態創建而成。
一、首先我們進行java動態代理的演示。
現在我們有一個簡單的業務接口Saying,如下:
package testAOP;
public interface Saying {
public void sayHello(String name);
public void talking(String name);
}
一個簡單的實現類SayingImpl,如下:
package testAOP;
public class SayingImpl implements Saying {
@Override
public void sayHello(String name) {
// TODO Auto-generated method stub
System.out.println(name + ":大家好啊!");
}
@Override
public void talking(String name) {
// TODO Auto-generated method stub
System.out.println(name + ":我的意思是,我們要努力建設和諧社會!");
}
}
我們要實現的是,在sayHello和talking之前和之后分別動態植入處理。
JDK動態代理主要用到java.lang.reflect包中的兩個類:Proxy和InvocationHandler.
InvocationHandler是一個接口,通過實現該接口定義橫切邏輯,并通過反射機制調用目標類的代碼,動態的將橫切邏輯和業務邏輯編織在一起。
Proxy利用InvocationHandler動態創建一個符合某一接口的實例,生成目標類的代理對象。
如下,我們創建一個InvocationHandler實例:
package testAOP;
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 {
//目標方法前執行
System.out.println("——————————————————————————");
System.out.println("下一位請登臺發言!");
//目標方法調用
Object obj = method.invoke(target, args);
//目標方法后執行
System.out.println("大家掌聲鼓勵!");
return obj;
}
}
下面是測試:
package testAOP;
import java.lang.reflect.Proxy;
public class JDKProxyTest {
public static void main(String[] args) {
// 希望被代理的目標業務類
Saying target = new SayingImpl();
// 將目標類和橫切類編織在一起
MyInvocationHandler handler = new MyInvocationHandler(target);
// 創建代理實例
Saying proxy = (Saying) Proxy.newProxyInstance(
target.getClass().getClassLoader(),//目標類的類加載器
target.getClass().getInterfaces(),//目標類的接口
handler);//橫切類
proxy.sayHello("小明");
proxy.talking("小麗");
}
}
運行情況如下:
——————————————————————————
下一位請登臺發言!
小明:大家好啊!
大家掌聲鼓勵!
——————————————————————————
下一位請登臺發言!
小麗:我的意思是,我們要努力建設和諧社會!
大家掌聲鼓勵!
使用JDK動態代理有一個很大的限制,就是它要求目標類必須實現了對應方法的接口,它只能為接口創建代理實例。我們在上文測試類中的Proxy的newProxyInstance方法中可以看到,該方法第二個參數便是目標類的接口。如果該類沒有實現接口,這就要靠cglib動態代理了。
CGLib采用非常底層的字節碼技術,可以為一個類創建一個子類,并在子類中采用方法攔截的技術攔截所有父類方法的調用,并順勢植入橫切邏輯。
二、接下來我們進行cglib動態代理的演示。
首先我們需要導包,我用的包是cglib-nodep-2.1_3.jar。
我們首先創建一個代理創建器CglibProxy:
package testAOP.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor{
Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
//設置需要創建的子類
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通過字節碼技術動態創建子類實例
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("下一位請登臺發言!");
//目標方法調用
Object result = proxy.invokeSuper(obj, args);
//目標方法后執行
System.out.println("大家掌聲鼓勵!");
return result;
}
}
然后進行測試:
package testAOP.cglib;
import testAOP.Saying;
import testAOP.SayingImpl;
public class CglibProxyTest {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
//通過動態生成子類的方式創建代理類
Saying target = (Saying) proxy.getProxy(SayingImpl.class);
target.sayHello("小明");
target.talking("小麗");
}
}
結果與JDK動態代理沒有任何區別。
JDK動態代理和CGLib動態代理都是運行時增強,通過將橫切代碼植入代理類的方式增強。與此不同的是AspectJ,它能夠在通過特殊的編譯器在編譯時期將橫切代碼植入增強,這樣的增強處理在運行時候更有優勢,因為JDK動態代理和CGLib動態代理每次運行都需要增強。