標籤:getc 兩種 擴充 類的方法 直接 main 增強 return 依賴
代理模式是指給某個對象提供一個代理對象,使用者不直接存取原對象而是通過代理對象間接訪問。
我們可以使用代理模式實現面向切面編程(AOP), 由動態代理將切面功能織入目標方法而不侵入調用方的業務代碼。
或者使用代理模式實現遠端程序呼叫(RPC), 調用方像調用本地方法一樣調用代理方法,而不必關心代理調用遠程方法細節。
JDK提供了基於反射機制的動態代理實現,而被廣泛使用的第三方庫CGLIB則基於位元組碼操作架構ASM實現動態代理。
本文將簡單介紹兩種動態代理使用方法,做拋磚引玉之用。
content:
代理模式
實現靜態代理模式是非常簡單的, 首先我們定義一個介面:
public interface MyInterface { void foo();}
編寫被代理的對象:
public class MyService implements MyInterface { @Override public void foo() { System.out.println("foo"); }}
在代理模式中被代理的類通常被稱作委託類。
編寫靜態代理:
public class MyProxy implements MyInterface { private MyInterface subject; MyProxy(MyInterface subject) { this.subject = subject; } @Override public void foo() { long start = System.System.currentTimeMillis(); subject.foo(); long elapseTime = System.currentTimeMillis() - start; System.out.println("elapse time: " + elapseTime); }}
靜態代理是指代理類在編譯時間產生,與之相對動態代理則是運行時組建代理程式類的位元組碼並載入到JVM中。
靜態代理的問題在於編寫代理類時必須瞭解介面細節, 即編寫MyProxy時必須瞭解MyInterface的定義。
以AOP架構為例,架構無法預先瞭解介面資訊只能在運行時根據Class對象建立代理對象,因此編寫此類架構必須要有動態代理機制的支援。
java動態代理
Java的反射機制可以在運行時建立類,Java標準庫中提供了基於反射的代理機制。
代理類應該實現java.lang.reflect.InvocationHandler介面,該介面只有一個方法: invoke。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
當我們通過代理訪問委託類方法時,會調用代理類的invoke方法。我們可以通過invoke方法的參數獲得調用資訊,並用反射機制調用為委託類的方法。
invoke方法的三個參數為:
Object porxy: 被呼叫者法的動態代理執行個體
Method method: 被調用的方法
Object[] args: 調用時傳入的參數
java.lang.reflect.Proxy.newProxyInstance方法用於建立代理對象,它使用反射機制在運行時建立了代理類並載入到JVM中。該方法有三個參數:
ClassLoader loader: 載入代理類的載入器
Class<?>[] interfaces: 代理類要實現的方法
InvocationHandler h: 在調用代理類方法時,嵌入調用過程的InvocationHandler執行個體
直接描述較難理解,我們來看代碼:
public class MyProxy implements InvocationHandler { private Object subject; public MyProxy(Object subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 1 return method.invoke(subject, args); } @SuppressWarnings("unchecked") public static <T> T createProxy(T subject) { return (T)Proxy.newProxyInstance(subject.getClass().getClassLoader(), // 2 subject.getClass().getInterfaces(), new MyProxy(subject)); } public static void main(String[] args) { MyInterface obj = createProxy(new MyService()); // 3 obj.foo(); }}
MyInterface和MyService和上文中完全相同:
public interface MyInterface { void foo();}public class MyService implements MyInterface { @Override public void foo() { System.out.println("foo"); }}
MyProxy類實現了InvocationHandler介面,並提供了createProxy靜態方法。
在main方法中我們為MyService對象建立了一個代理,並通過代理調用了foo方法。
在代碼中有幾處細節值得瞭解:
在main方法中通過obj.getClass().getName()獲得代理類的類名為:com.sun.proxy.$Proxy0。這個類名表示這個類是動態產生的Proxy類,0表示它是當前JVM中第一個動態代理類。
- 在注釋2處我們通過
newProxyInstance建立了代理類即上文中的$Proxy0類:
loader為被代理類的載入器,也可以使用MyInterface.class.getClassLoader(),它們都是AppClassLoader執行個體。
interfaces參數為subject.getClass().getInterfaces()表示動態代理類$Proxy0實現了subject的所有介面,但$Proxy0不是Subject類的子類。
因此在main方法中使用MyInterface obj = createProxy(new MyService());而不能使用MyService obj = createProxy(new MyService());。
- 我們使用
MyProxy執行個體作為InvocationHandler攔截動態代理類所有方法調用。
- 在注釋1處我們實現了
invoke方法攔截動態代理對象所有方法調用:
- 傳入的實參
Object proxy是動態代理類的執行個體,即main方法中的obj, 它是$Proxy0類的執行個體。注意InvocationHandler執行個體不是動態代理執行個體,handler是編譯產生的靜態類。
- 傳入的實參
Method method是介面類的Method對象,即MyInterface.class.getMethod("foo")而非委託類MyService的Method對象
method.invoke(subject, args)用反射的方式調用了被代理執行個體的方法,我們可以在invoke方法中添加其它代碼以增強委託類的功能
cglib動態代理
CGLIB是一個強大的動態代理庫,SpringAOP和dynaop架構使用CGLIB進行方法攔截和增強,Hibernate使用CGLIB進行代理關聯,此外JMock也使用CGLIB提供Mock對象。
在實現上,CGLIB使用高效能輕量級位元組碼操作架構ASM來動態組建代理程式類。值得一提的是,CGLIB比JDK動態代理還要快。
CGLIB將動態代理類實現為委託類的子類,因此可以代理沒有實現介面或對介面進行了擴充的類。因為子類無法覆蓋final方法,因此只能代理非final方法。
首先使用maven匯入cglib依賴:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.6</version></dependency>
編寫CGLIB動態代理:
public class CglibProxy { @SuppressWarnings("unchecked") public static <T> T createProxy(T subject) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(subject.getClass()); enhancer.setCallback(new MyInterceptor()); return (T) enhancer.create(); } private static class MyInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } } public static void main(String[] args) { MyService obj = createProxy(new MyService()); obj.foo(); }}
Enhancer用於對委託類進行增強,它可以攔截方法調用以實現代理機制。
MethodInterceptor.intercept方法用於攔截方法調用,它的幾個參數為:
Object obj: 動態代理對象
Method method: 被攔截的方法,本例main方法中被攔截的方法是: MyService.class.getMethod("foo")
Object[] args: 調用實參
MethodProxy proxy: 用來調用委託類方法的快捷代理,不是動態代理類。
樣本中定義的動態代理沒有添加任何額外功能,這種特殊的Callback可以使用net.sf.cglib.proxy.NoOp代替:
public static <T> T createProxy(T subject) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(subject.getClass()); enhancer.setCallback(NoOp.INSTANCE); return (T) enhancer.create(); }
Enhancer可以使用CallbackFilter為每個方法調用配置過濾器:
class MyService { void foo0() { System.out.println("0"); } void foo1() { System.out.println("1"); }}public class CglibProxy { @SuppressWarnings("unchecked") public static <T> T createProxy(T subject) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(subject.getClass()); Callback callback0 = NoOp.INSTANCE; Callback callback1 = NoOp.INSTANCE; enhancer.setCallbacks(new Callback[]{callback0, callback1}); enhancer.setCallbackFilter(new MyCallbackFilter()); return (T) enhancer.create(); } private static class MyCallbackFilter implements CallbackFilter { @Override public int accept(Method method) { if ("foo0".equals(method.getName())) { return 0; } else { return 1; } } } public static void main(String[] args) { MyService obj = createProxy(new MyService()); obj.foo0(); obj.foo1(); }}
當Enhancer設定了CallbackFilter之後,在攔截方法前會先調用CallbackFilter.accept()方法判斷使用哪個Callback執行個體進行攔截。
CallbackFilter.accept()傳回值即為攔截器的下標,即若accept方法返回0,則調用enhancer.callbacks[0]進行攔截。
Java動態代理