一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 深入理解JDK動態代理

深入理解JDK動態代理

2021-04-22 23:22安琪拉的博客 Java教程

動態代理的優勢是實現無侵入式的代碼擴展,做方法的增強;讓你可以在不用修改源碼的情況下,增強一些方法;在方法的前后你可以做你任何想做的事情(甚至不去執行這個方法就可以)。

深入理解JDK動態代理

代理模式的目的是在不修改原有類方法設計的基礎上,對方法行為進行增強。

為了好理解,舉個實際場景,我們業務場景中經常有限流的需求,常規操作是在需要限流的接口代碼前加入調用次數判斷的代碼,但是這樣每個需要限流的方法都需要加,工作量大不說,一方面不好維護,不能很清晰的知道每個接口限流值,另一方面,限流代碼和業務代碼堆疊在一起,也影響代碼閱讀。解法是做一套統一限流,一般好點的會有專門的接口限流平臺,配置對應的接口名,設置限流值,直接就可以限流,實現方式就可以用動態代理。不修改原接口的實現,對接口進行增強。

動態代理的優勢是實現無侵入式的代碼擴展,做方法的增強;讓你可以在不用修改源碼的情況下,增強一些方法;在方法的前后你可以做你任何想做的事情(甚至不去執行這個方法就可以)。

靜態代理

 

既然有動態,那一定有靜態,說下區別吧,

靜態:最大的區別是靜態是編譯期就決定了,在程序運行之前,代理類的.class文件已經存在了。被代理類是什么,代理類實現方式。

舉個栗子:

我現在有個接口,是把Json字符串解析成Object 對象,接口如下:

  1. public interface IProvider { 
  2.     
  3.   Object getData(String json); 
  4.    

接口的實現類如下:

  1. public class SimpleProvider implements IProvider { 
  2.     @Override 
  3.     public Object getData(String json) { 
  4.         //解析json 拿到數據 
  5.         return parseJson(json); 
  6.     } 

那現在有個需求,需要對 getData 方法做限流,指定用靜態代理的方式。

需要很簡單,我就直接貼了:

  1. public class ProviderProxy implements IProvider{ 
  2.  
  3.     //持有一個被代理對象的引用(在這里是SimpleProvider) 
  4.     IProvider iProvider; 
  5.  
  6.     public StaticProviderProxy(IProvider iProvider){ 
  7.         this.iProvider = iProvider; 
  8.     } 
  9.  
  10.     @Override 
  11.     public Object getData(String json) { 
  12.         //做限流檢查 
  13.         if(callSpeed > flowLimt) { 
  14.           //流量超限 
  15.            throw FlowLimitException(); 
  16.         } 
  17.         Object object = iProvider.getData(json); 
  18.         return object; 
  19.     } 
  20. //main  
  21. public static void main(String[] args) { 
  22.   IProvider provider = new ProviderProxy(new SimpleProvider()); 
  23.     provider.getData("{\"data\":{}}"); 

這就是靜態代理,代理類(ProviderProxy)實現和需要做方法增強的被代理類(SimpleProvider)實現同一個接口(IProvider),方法具體實現上做增強,這里是限流檢查。

動態代理

 

Java 動態代理

  • 動態代理類:在程序運行時,通過反射機制動態生成。
  • 動態代理類通常代理接口下的所有類。靜態一般指定某個類代理。
  • 動態代理事先不知道要代理的是什么,只有在運行的時候才能確定。靜態是編譯期確定的。

還是以IProvider 接口為例,同樣是要對 SimpleProvider 做增強,如下:

  1. public class ProviderHandler implements InvocationHandler { 
  2.     Object target; 
  3.  
  4.     public Object bind(Object target){ 
  5.         this.target = target; 
  6.         //這里生成了代理對象 
  7.         return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
  8.                 target.getClass().getInterfaces(), this); 
  9.     } 
  10.  
  11.     @Override 
  12.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  13.         //限流 
  14.         flowLimit(args); 
  15.         Object obj = method.invoke(target, args); 
  16.         //打印日志 
  17.         logger.info("print log..."); 
  18.         return obj; 
  19.     } 
  20. //main 
  21. public static void main(String[] args) { 
  22.    ProviderHandler providerHandler = new ProviderHandler(); 
  23.    IProvider iProvider = (IProvider) providerHandler.bind(new SimpleProvider()); 
  24.    iProvider.getData("weibo.data"); 

這里有三個對象:

SimpleProvider 對象 , 我們稱之為被代理對象

ProviderHandler 對象,我們稱之為執行者對象

Proxy對象 (通過在ProviderHandler bind方法中使用Proxy.newProxyInstance生成的對象) 我們稱之為代理對象

這三個對象是什么關系呢?

Proxy是真正的代理類,SimpleProvider是被代理類,ProviderHandler是執行方法增強的執行者。

我們是為了增強SimpleProvider (被代理對象)的getData方法,就Proxy對象來代理被代理對象的執行,Proxy不親自來做這件事,而是交給執行者對象ProviderHandler 來實現增加的目錄,執行調用前的限流校驗。

實際怎么實現的呢?

newProxyInstance源碼

  1. public static Object newProxyInstance(ClassLoader loader, 
  2.                                           Class<?>[] interfaces, 
  3.                                           InvocationHandler h) 
  4.         throws IllegalArgumentException 
  5.     { 
  6.         //對 Invocationhandler做判空處理 
  7.         Objects.requireNonNull(h); 
  8.         //復制[IProvider接口] 
  9.         final Class<?>[] intfs = interfaces.clone(); 
  10.  
  11.        //根據IProvider的類加載器IProvider接口生成了Proxy類,關鍵:根據類加載器和接口對象在JVM緩存中生成一個類對象 
  12.         Class<?> cl = getProxyClass0(loader, intfs); 
  13.         //獲取構造器 
  14.         final Constructor<?> cons = cl.getConstructor(constructorParams); 
  15.         //保存InvocationHandler的引用 
  16.         final InvocationHandler ih = h; 
  17.         //通過構造器實例化Proxy代理對象 
  18.         return cons.newInstance(new Object[]{h}); 
  19.     } 

代碼注釋寫的很清晰。

可能這個地方大家都會疑惑,生成的Proxy對象是怎樣調用執行者的invoke函數的。

這個地方通過這段代碼將Proxy0的class字節碼輸出到文件。

  1. byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", WeiboProvider.class.getInterfaces()); 
  2. String path = "C:**/IdeaProjects/study/out/production/study/SimpleProxy.class"
  3. try(FileOutputStream fos = new FileOutputStream(path)) { 
  4.     fos.write(classFile); 
  5.     fos.flush(); 
  6.     System.out.println("代理類class文件寫入成功"); 
  7.    } catch (Exception e) { 
  8.      System.out.println("寫文件錯誤"); 
  9.  } 

反編譯Proxy0如下:

  1. //Proxy0 是動態生成的類,繼承自Proxy,實現了IProvider接口 
  2. public final class $Proxy0 extends Proxy implements IProvider { 
  3.     private static Method m1; 
  4.     private static Method m2; 
  5.     private static Method m3; 
  6.     private static Method m0; 
  7.  
  8.     public $Proxy0(InvocationHandler var1) throws  { 
  9.         super(var1); 
  10.     } 
  11.  
  12.     public final boolean equals(Object var1) throws  { 
  13.         try { 
  14.             return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue(); 
  15.         } catch (RuntimeException | Error var3) { 
  16.             throw var3; 
  17.         } catch (Throwable var4) { 
  18.             throw new UndeclaredThrowableException(var4); 
  19.         } 
  20.     } 
  21.  
  22.     public final String toString() throws  { 
  23.         try { 
  24.             return (String)super.h.invoke(this, m2, (Object[])null); 
  25.         } catch (RuntimeException | Error var2) { 
  26.             throw var2; 
  27.         } catch (Throwable var3) { 
  28.             throw new UndeclaredThrowableException(var3); 
  29.         } 
  30.     } 
  31.  
  32.     public final String getData(String var1) throws  { 
  33.         try { 
  34.             //m3就是IProvider 接口的getData方法  
  35.             //super.h 是父類java.lang.reflect.Proxy的屬性 InvocationHandler 
  36.             return (String)super.h.invoke(this, m3, new Object[]{var1}); 
  37.         } catch (RuntimeException | Error var3) { 
  38.             throw var3; 
  39.         } catch (Throwable var4) { 
  40.             throw new UndeclaredThrowableException(var4); 
  41.         } 
  42.     } 
  43.  
  44.     public final int hashCode() throws  { 
  45.         try { 
  46.             return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue(); 
  47.         } catch (RuntimeException | Error var2) { 
  48.             throw var2; 
  49.         } catch (Throwable var3) { 
  50.             throw new UndeclaredThrowableException(var3); 
  51.         } 
  52.     } 
  53.  
  54.     static { 
  55.         try { 
  56.             m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")}); 
  57.             m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 
  58.             //m3就是IProvider 接口的getData方法 
  59.             m3 = Class.forName("aop.IProvider").getMethod("getData", new Class[]{Class.forName("java.lang.String")}); 
  60.             m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 
  61.         } catch (NoSuchMethodException var2) { 
  62.             throw new NoSuchMethodError(var2.getMessage()); 
  63.         } catch (ClassNotFoundException var3) { 
  64.             throw new NoClassDefFoundError(var3.getMessage()); 
  65.         } 
  66.     } 

重點在 return (String)super.h.invoke(this, m3, new Object[]{var1});代碼。

$Proxy0繼承Proxy類,實現了IProvider接口,所以也有getData()函數,而getData函數調用的是執行者InvocationHandler的invoke方法,m3是通過反射拿到的Method對象,所以看getData調用invoke傳遞的。三個參數,第一個是Proxy對象,第二個是getData方法對象,第三個是參數。

總結一下:

  • 動態代理的本質就是,生成一個繼承自Proxy,實現被代理接口(IProvider)的類 - Proxy0。
  • Proxy0 持有InvocationHandler實例,InvocationHandler 持有SimpleProvider實例。Proxy0調用接口 getData方法時,先傳遞給InvocationHandler,InvocationHandler再傳遞給SimpleProvider實例。

動態代理實際上就是幫我們在JVM內存中直接重新生成了代理類class和對應類對象,然后通過執行者InvocationHandler調用被代理對象SimpleProvider。

Spring AOP中的代理

Spring代理其實是對JDK動態代理和CGLIB代理進行了封裝,并且引入了AOP的概念,同時引入了AspectJ中的一些注解:@pointCut @After 等。

  1. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { 
  2.            if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { 
  3.    Class<?> targetClass = config.getTargetClass(); 
  4.    if (targetClass == null) { 
  5.     throw new AopConfigException("TargetSource cannot determine target class: " + 
  6.       "Either an interface or a target is required for proxy creation."); 
  7.    } 
  8.       // 如果是接口,使用jdk代理  
  9.    if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { 
  10.     return new JdkDynamicAopProxy(config); 
  11.    } 
  12.       //否則使用cglib 
  13.    return new ObjenesisCglibAopProxy(config); 
  14.   } 
  15.   else { 
  16.    return new JdkDynamicAopProxy(config); 
  17.   } 
  18.  } 

原文地址:https://mp.weixin.qq.com/s/34LAnTGhqe7DTYmT1PRQJg

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 91tv破解版不限次数 | 黑人粗又长| 精品一久久香蕉国产二月 | 男女拍拍拍免费视频网站 | 国产精品自在线拍 | 美女全身体光羞羞漫画 | 久久久精品成人免费看 | 国产乱插 | 日本在线视频免费看 | 激情视频在线播放 | 天堂在线观看中文字幕 | 亚洲精品视频免费在线观看 | 厨房里摸着乳丰满在线观看 | 日本大片免aaa费观看视频 | 国产精品青青青高清在线观看 | 久久偷拍国2017的 | 无人影院在线播放 | 国产精品调教 | 国产美女亚洲精品久久久久久 | 国产caonila在线观看 | 四虎影院地址 | 香蕉eeww99国产在线观看 | 精品久久洲久久久久护士免费 | 国产成人综合视频 | 婷婷在线综合 | 午夜国产精品福利在线观看 | 国产精品久久久久不卡绿巨人 | 99在线精品免费视频 | 国产精品天天看特色大片不卡 | 国产馆| 99在线观看国产 | 欧美高清videosex极品 | 国产二区精品视频 | 草留色区 | a v在线男人的天堂观看免费 | 国产欧美成人免费观看 | 亚洲成年人在线观看 | 四虎影院新地址 | 成人影院vs一区二区 | 法国老妇性xx在线播放 | 好湿好紧太硬了我好爽 |