標籤:
代理的概念:簡單的理解就是通過為某一個對象建立一個代理對象,我們不直接引用原本的對象,而是由建立的代理對象來控制對原對象的引用。
動態代理:是指在程式運行時由Java反射機制動態產生,無需手動編寫代碼。動態代理不僅簡化了編程工作,而且提高了軟體系統的可擴充性,因為Java反射機制可以產生任意類型的動態代理類。
代理原理:代理對象內部含有對真實對象的引用,從而可以操作真實對象,同時代理對象提供與真實對象相同的介面以便在任何時刻都能代替真實對象。同時,代理對象可以在執行真實對象操作時,附加其他的操作,相當於對真實對象進行封裝。
以下通過一個簡單的例子來看基於JDK動態代理與CGLIB分別如何?代理。
代理介面UserService:
1 package com.liang.test;2 3 public interface UserService {4 public void say(String arg);5 }
UserService實作類別UserServiceImpl:
1 package com.liang.test;2 3 public class UserServiceImpl implements UserService {4 @Override5 public void say(String arg) {6 System.out.println("hello, I am " + arg);7 }8 }
JDK通過java.lang.reflect包下的Proxy類和一個InvocationHandler介面來產生動態代理類和動態代理對象。
首先建立一個實現InvocationHandler介面的調用控制器對象MyInvocationHandler:(當執行動態代理對象裡的目標方法時,實際上會替換成調用MyInvocationHandler的invoke方法)
1 package com.liang.test; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 6 public class MyInvocationHandler implements InvocationHandler { //實現InvocationHandler 7 8 private Object object; //被代理對象 9 10 public MyInvocationHandler(Object object){ //接收被代理對象11 this.object = object;12 }13 @Override14 public Object invoke(Object proxy, Method method, Object[] args)15 throws Throwable { //say()方法被替換執行成當前方法,可在其中添加額外功能增強目標方法16 System.out.println("before method");17 Object obj = method.invoke(object, args); //通過反射調用UserService的say()方法18 System.out.println("after method");19 return obj;20 }21 22 }
通過Proxy結合MyInvocationHandler為UserService建立動態代理類:
1 package com.liang.test; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Proxy; 5 6 public class TestProxy { 7 8 public static void main(String[] args) { 9 UserService userService = new UserServiceImpl();10 InvocationHandler handler = new MyInvocationHandler(userService);11 12 UserService proxy = (UserService)Proxy.newProxyInstance(userService.getClass()13 .getClassLoader(), userService.getClass().getInterfaces(),14 handler);15 proxy.say("孫悟空"); //調用代理執行個體16 }17 18 }
運行結果:
before methodhello, I am 孫悟空after method
可以看到執行代理執行個體的say方法時執行的是MyInvocationHandler的invoke()方法。
而CGLIB則是採用非常底層的位元組碼技術,為被代理對象建立一個子類,並在子類中採用方法攔截的技術攔截所有父類方法的調用,並順勢植入增強代碼。CGLIB實現動態代理的樣本如下:
1 package com.liang.test; 2 3 import java.lang.reflect.Method; 4 5 import net.sf.cglib.proxy.Enhancer; 6 import net.sf.cglib.proxy.MethodInterceptor; 7 import net.sf.cglib.proxy.MethodProxy; 8 9 public class CglibProxy implements MethodInterceptor {10 11 private Enhancer enhancer = new Enhancer();12 13 public Object getProxy(Class clazz) {14 enhancer.setSuperclass(clazz); // 設定需要建立子類的類15 enhancer.setCallback(this);16 return enhancer.create();// 建立子類執行個體17 }18 19 @Override20 public Object intercept(Object object, Method method, Object[] args,21 MethodProxy proxy) throws Throwable { //攔截父類中所有的方法調用22 System.out.println("cglib: before method");23 Object obj = proxy.invokeSuper(object, args); //通過代理類調用父類中的方法24 System.out.println("cglib: after method");25 return obj;26 }27 28 }
測試代碼:
1 package com.liang.test; 2 3 public class TestProxy { 4 5 public static void main(String[] args) { 6 CglibProxy proxy = new CglibProxy(); 7 UserServiceImpl userServiceImpl = (UserServiceImpl)proxy.getProxy(UserServiceImpl.class); 8 userServiceImpl.say("孫悟空"); 9 }10 11 }
運行結果:
cglib: before methodhello, I am 孫悟空cglib: after method
小結:JDK動態代理與CGLIB動態代理最常見的應用是SpringAOP的底層實現,基於JDK的動態代理在建立代理對象時所花費的時間比CGLIB短,但是JDK只能為介面建立代理執行個體,這一點可以從newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)中看出:第二個入參就是代理樣本實現的介面列表。而CGLIB建立的代理對象效能比JDK建立的代理對象高,但是花費的時間相對長一些,並且由於CGLIB採用動態建立子類的方式組建代理程式對象,所以對於由final修飾的方法不能通過CGLIB進行代理。基於此,我們對於singgleton的代理對象或者具有執行個體池的代理比較適合使用CGLIB動態代理技術,對於每次都要new出新執行個體的一般採用JDK的動態代理技術。
本次學習資料參照了:
1.《精通Spring2.x-公司專屬應用程式開發詳解》——陳雄華著;
2.http://rejoy.iteye.com/blog/1627405——JDK動態代理實現原理
Java學習之:JDK動態代理與CGLIB動態代理