JDK1.3之后,Java提供了動態代理的技術,允許開發者在運行期間創建接口的代理實例。
一、首先我們進行JDK動態代理的演示。
現在我們有一個簡單的業務接口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動態代理沒有任何區別。
CGLib動態代理能代理類和接口,但是不能代理final類,也是有一定局限性。
JDK動態代理和CGLib動態代理都是運行時增強,通過將橫切代碼植入代理類的方式增強。與此不同的是AspectJ,它能夠在通過特殊的編譯器在編譯時期將橫切代碼植入增強,這樣的增強處理在運行時候更有優勢,因為JDK動態代理和CGLib動態代理每次運行都需要增強。