代理的概念:簡單的理解就是通過為某一個對象創建一個代理對象,我們不直接引用原本的對象,而是由創建的代理對象來控制對原對象的引用。
動態代理:是指在程序運行時由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.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.VEvb.com/blog/1627405——JDK動態代理實現原理
新聞熱點
疑難解答