一、動態代理概念 動態代理分為JDK動態代理和cglib動態代理兩種方式。 jdk動態代理是由Java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的。 總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行緩存,這 ...
一、動態代理概念
動態代理分為JDK動態代理和cglib動態代理兩種方式。
jdk動態代理是由Java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的。
總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行緩存,這樣解決asm生成類過程低效問題)。
還有一點必須註意:jdk動態代理的應用前提,必須是目標類基於統一的介面。如果沒有上述前提,jdk動態代理不能應用。
由此可以看出,jdk動態代理有一定的局限性,cglib這種第三方類庫實現的動態代理應用更加廣泛,且在效率上更有優勢。
二、JDK動態代理
以下代碼使用代理模式實現一個大小寫字元轉換的功能。
定義介面和實現類:
ISomeService介面:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk; /** * 介面類 * * @author Root */ public interface ISomeService { String doFirst(); void doSecond(); }
SomeServiceImpl實現類:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk; /** * 實現類 * * @author Root */ public class SomeServiceImpl implements ISomeService { @Override public String doFirst() { System.out.println("執行doFirst()..."); String result = "abcde"; return result; } @Override public void doSecond() { System.out.println("執行doSecond()..."); } }
JDK動態代理類:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { final ISomeService target = new SomeServiceImpl(); // 使用JDK的Proxy動態代理,要求目標類和代理類必須實現相同的介面,因為其底層的執行原理與靜態代理的相同 ISomeService service = (ISomeService) Proxy.newProxyInstance( // 目標類的類載入器 target.getClass().getClassLoader(), // 目標類所實現的所有介面 target.getClass().getInterfaces(), new InvocationHandler() { // proxy:代理對象 // method:目標方法 // args:目標方法的參數列表 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 調用目標方法 Object result = method.invoke(target, args); if (result != null) { result = ((String) result).toUpperCase(); } return result; } }); String result = service.doFirst(); System.out.println(result); service.doSecond(); } }
三、cglib動態代理
Cglib是一個優秀的動態代理框架,它的底層使用ASM在記憶體中動態的生成被代理類的子類,使用CGLIB即使代理類沒有實現任何介面也可以實現動態代理功能。CGLIB具有簡單易用,它的運行速度要遠遠快於JDK的Proxy動態代理:
CGLIB的核心類:
net.sf.cglib.proxy.Enhancer – 主要的增強類
net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類,它是Callback介面的子介面,需要用戶實現
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method類的代理類,可以方便的實現對源對象方法的調用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);//雖然第一個參數是被代理對象,也不會出現死迴圈的問題。
net.sf.cglib.proxy.MethodInterceptor介面是最通用的回調(callback)類型,它經常被基於代理的AOP用來實現攔截(intercept)方法的調用。這個介面只定義了一個方法
public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;
第一個參數是代理對像,第二和第三個參數分別是攔截的方法和方法的參數。原來的方法可能通過使用java.lang.reflect.Method對象的一般反射調用,或者使用 net.sf.cglib.proxy.MethodProxy對象調用。net.sf.cglib.proxy.MethodProxy通常被首選使用,因為它更快。
以下程式實現了大小寫轉換的功能:
實現類SomeService:
package com.ietree.basicskill.designpattern.dynamicproxy.cglib; /** * 實現類 * * @author Root */ public class SomeService { public String doFirst() { System.out.println("執行doFirst()..."); String result = "abcde"; return result; } public void doSecond() { System.out.println("執行doSecond()..."); } }
代理類MyCglibFactory:
package com.ietree.basicskill.designpattern.dynamicproxy.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 MyCglibFactory implements MethodInterceptor { private SomeService target; public MyCglibFactory() { super(); target = new SomeService(); } public SomeService myCglibCreator() { // 創建增強器對象 Enhancer enhancer = new Enhancer(); // 指定目標類,即父類 enhancer.setSuperclass(SomeService.class); // 設置回調介面對象 enhancer.setCallback(this); return (SomeService) enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 調用目標方法 Object result = method.invoke(target, args); if (result != null) { result = ((String) result).toUpperCase(); } return result; } }
測試:
package com.ietree.basicskill.designpattern.dynamicproxy.cglib; public class Main { public static void main(String[] args) { SomeService service = new MyCglibFactory().myCglibCreator(); String result = service.doFirst(); System.out.println("result = " + result); service.doSecond(); } }
運行結果:
執行doFirst()... result = ABCDE 執行doSecond()...