JDK和CGLIB動態代理原理區別


JDK和CGLIB動態代理原理區別

https://blog.csdn.net/yhl_jxy/article/details/80635012

版權聲明:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/yhl_jxy/article/details/80635012

一 JDK和CGLIB動態代理原理

JDK動態代理:

利用攔截器(攔截器必須實現InvocationHanlder)加上反射機制生成一個實現代理接口的匿名類,

在調用具體方法前調用InvokeHandler來處理。

CGLiB動態代理:

利用ASM開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。

 

何時使用JDK還是CGLiB?

1、如果目標對象實現了接口,默認情況下會采用JDK的動態代理實現AOP。

2、如果目標對象實現了接口,可以強制使用CGLIB實現AOP。

3、如果目標對象沒有實現了接口,必須采用CGLIB庫,Spring會自動在JDK動態代理和CGLIB之間轉換。


如何強制使用CGLIB實現AOP?

1、添加CGLIB庫(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)

2、在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>


JDK動態代理和CGLIB字節碼生成的區別?

1、JDK動態代理只能對實現了接口的類生成代理,而不能針對類。

2、CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,

     並覆蓋其中方法實現增強,但是因為采用的是繼承,所以該類或方法最好不要聲明成final,

     對於final類或方法,是無法繼承的。


CGlib比JDK快?

1、使用CGLib實現動態代理,CGLib底層采用ASM字節碼生成框架,使用字節碼技術生成代理類,

在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能對聲明為final的方法進行代理,

因為CGLib原理是動態生成被代理類的子類。

2、在jdk6、jdk7、jdk8逐步對JDK動態代理優化之后,在調用次數較少的情況下,JDK代理效率高於CGLIB代理效率,

只有當進行大量調用的時候,jdk6和jdk7比CGLIB代理效率低一點,但是到jdk8的時候,jdk代理效率高於CGLIB代理,

總之,每一次jdk版本升級,jdk代理效率都得到提升,而CGLIB代理消息確有點跟不上步伐。


Spring如何選擇用JDK還是CGLiB?

1、當Bean實現接口時,Spring就會用JDK的動態代理。

2、當Bean沒有實現接口時,Spring使用CGlib是實現。

3、可以強制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。

 
二 代碼實例

接口:

  1. package com.lanhuigu.spring.proxy.compare;
  2.  
  3. /**
  4. * 用戶管理接口(真實主題和代理主題的共同接口,這樣在任何可以使用真實主題的地方都可以使用代理主題代理。)
  5. * --被代理接口定義
  6. */
  7. public interface IUserManager {
  8. void addUser(String id, String password);
  9. }

實現類:

  1. package com.lanhuigu.spring.proxy.compare;
  2.  
  3. /**
  4. * 用戶管理接口實現(被代理的實現類)
  5. */
  6. public class UserManagerImpl implements IUserManager {
  7.  
  8. @Override
  9. public void addUser(String id, String password) {
  10. System.out.println( "======調用了UserManagerImpl.addUser()方法======");
  11. }
  12. }

JDK代理實現:

  1. package com.lanhuigu.spring.proxy.compare;
  2.  
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6.  
  7. /**
  8. * JDK動態代理類
  9. */
  10. public class JDKProxy implements InvocationHandler {
  11. /** 需要代理的目標對象 */
  12. private Object targetObject;
  13.  
  14. /**
  15. * 將目標對象傳入進行代理
  16. */
  17. public Object newProxy(Object targetObject) {
  18. this.targetObject = targetObject;
  19. //返回代理對象
  20. return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
  21. targetObject.getClass().getInterfaces(), this);
  22. }
  23.  
  24. /**
  25. * invoke方法
  26. */
  27. @Override
  28. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  29. // 一般我們進行邏輯處理的函數比如這個地方是模擬檢查權限
  30. checkPopedom();
  31. // 設置方法的返回值
  32. Object ret = null;
  33. // 調用invoke方法,ret存儲該方法的返回值
  34. ret = method.invoke(targetObject, args);
  35. return ret;
  36. }
  37.  
  38. /**
  39. * 模擬檢查權限的例子
  40. */
  41. private void checkPopedom() {
  42. System.out.println( "======檢查權限checkPopedom()======");
  43. }
  44. }

CGLIB代理實現:

  1. package com.lanhuigu.spring.proxy.compare;
  2.  
  3. import net.sf.cglib.proxy.Enhancer;
  4. import net.sf.cglib.proxy.MethodInterceptor;
  5. import net.sf.cglib.proxy.MethodProxy;
  6.  
  7. import java.lang.reflect.Method;
  8.  
  9. /**
  10. * CGLibProxy動態代理類
  11. */
  12. public class CGLibProxy implements MethodInterceptor {
  13. /** CGLib需要代理的目標對象 */
  14. private Object targetObject;
  15.  
  16. public Object createProxyObject(Object obj) {
  17. this.targetObject = obj;
  18. Enhancer enhancer = new Enhancer();
  19. enhancer.setSuperclass(obj.getClass());
  20. enhancer.setCallback( this);
  21. Object proxyObj = enhancer.create();
  22. // 返回代理對象
  23. return proxyObj;
  24. }
  25.  
  26. @Override
  27. public Object intercept(Object proxy, Method method, Object[] args,
  28. MethodProxy methodProxy) throws Throwable {
  29. Object obj = null;
  30. // 過濾方法
  31. if ("addUser".equals(method.getName())) {
  32. // 檢查權限
  33. checkPopedom();
  34. }
  35. obj = method.invoke(targetObject, args);
  36. return obj;
  37. }
  38.  
  39. private void checkPopedom() {
  40. System.out.println( "======檢查權限checkPopedom()======");
  41. }
  42. }

客戶端測試類:

  1. package com.lanhuigu.spring.proxy.compare;
  2.  
  3. /**
  4. * 代理模式[[ 客戶端--》代理對象--》目標對象 ]]
  5. */
  6. public class Client {
  7. public static void main(String[] args) {
  8. System.out.println( "**********************CGLibProxy**********************");
  9. CGLibProxy cgLibProxy = new CGLibProxy();
  10. IUserManager userManager = (IUserManager) cgLibProxy.createProxyObject( new UserManagerImpl());
  11. userManager.addUser( "lanhuigu", "123456");
  12.  
  13. System.out.println( "**********************JDKProxy**********************");
  14. JDKProxy jdkPrpxy = new JDKProxy();
  15. IUserManager userManagerJDK = (IUserManager) jdkPrpxy.newProxy( new UserManagerImpl());
  16. userManagerJDK.addUser( "lanhuigu", "123456");
  17. }
  18. }

程序運行結果:

三 JDK和CGLIB動態代理總結

JDK代理是不需要第三方庫支持,只需要JDK環境就可以進行代理,使用條件:

1、實現InvocationHandler 

2、使用Proxy.newProxyInstance產生代理對象

3、被代理的對象必須要實現接口

CGLib必須依賴於CGLib的類庫,但是它需要類來實現任何接口代理的是指定的類生成一個子類,

覆蓋其中的方法,是一種繼承但是針對接口編程的環境下推薦使用JDK的代理;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM