成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

深入理解JDK動態(tài)代理

開發(fā) 項(xiàng)目管理
動態(tài)代理的優(yōu)勢是實(shí)現(xiàn)無侵入式的代碼擴(kuò)展,做方法的增強(qiáng);讓你可以在不用修改源碼的情況下,增強(qiáng)一些方法;在方法的前后你可以做你任何想做的事情(甚至不去執(zhí)行這個方法就可以)。

代理模式的目的是在不修改原有類方法設(shè)計(jì)的基礎(chǔ)上,對方法行為進(jìn)行增強(qiáng)。

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

動態(tài)代理的優(yōu)勢是實(shí)現(xiàn)無侵入式的代碼擴(kuò)展,做方法的增強(qiáng);讓你可以在不用修改源碼的情況下,增強(qiáng)一些方法;在方法的前后你可以做你任何想做的事情(甚至不去執(zhí)行這個方法就可以)。

靜態(tài)代理

既然有動態(tài),那一定有靜態(tài),說下區(qū)別吧,

靜態(tài):最大的區(qū)別是靜態(tài)是編譯期就決定了,在程序運(yùn)行之前,代理類的.class文件已經(jīng)存在了。被代理類是什么,代理類實(shí)現(xiàn)方式。

舉個栗子:

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

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

接口的實(shí)現(xiàn)類如下:

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

那現(xiàn)在有個需求,需要對 getData 方法做限流,指定用靜態(tài)代理的方式。

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

  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\":{}}"); 

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

動態(tài)代理

Java 動態(tài)代理

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

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

  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 對象,我們稱之為執(zhí)行者對象

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

這三個對象是什么關(guān)系呢?

Proxy是真正的代理類,SimpleProvider是被代理類,ProviderHandler是執(zhí)行方法增強(qiáng)的執(zhí)行者。

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

實(shí)際怎么實(shí)現(xiàn)的呢?

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.         //復(fù)制[IProvider接口] 
  9.         final Class<?>[] intfs = interfaces.clone(); 
  10.  
  11.        //根據(jù)IProvider的類加載器IProvider接口生成了Proxy類,關(guān)鍵:根據(jù)類加載器和接口對象在JVM緩存中生成一個類對象 
  12.         Class<?> cl = getProxyClass0(loader, intfs); 
  13.         //獲取構(gòu)造器 
  14.         final Constructor<?> cons = cl.getConstructor(constructorParams); 
  15.         //保存InvocationHandler的引用 
  16.         final InvocationHandler ih = h; 
  17.         //通過構(gòu)造器實(shí)例化Proxy代理對象 
  18.         return cons.newInstance(new Object[]{h}); 
  19.     } 

代碼注釋寫的很清晰。

可能這個地方大家都會疑惑,生成的Proxy對象是怎樣調(diào)用執(zhí)行者的invoke函數(shù)的。

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

  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 是動態(tài)生成的類,繼承自Proxy,實(shí)現(xiàn)了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.     } 

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

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

總結(jié)一下:

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

動態(tài)代理實(shí)際上就是幫我們在JVM內(nèi)存中直接重新生成了代理類class和對應(yīng)類對象,然后通過執(zhí)行者InvocationHandler調(diào)用被代理對象SimpleProvider。

Spring AOP中的代理

Spring代理其實(shí)是對JDK動態(tài)代理和CGLIB代理進(jìn)行了封裝,并且引入了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.  } 

 

責(zé)任編輯:武曉燕 來源: 安琪拉的博客
相關(guān)推薦

2010-06-01 15:25:27

JavaCLASSPATH

2016-12-08 15:36:59

HashMap數(shù)據(jù)結(jié)構(gòu)hash函數(shù)

2020-07-21 08:26:08

SpringSecurity過濾器

2023-12-06 08:23:44

代理模式設(shè)計(jì)模式

2023-10-19 11:12:15

Netty代碼

2021-02-17 11:25:33

前端JavaScriptthis

2009-09-25 09:14:35

Hibernate日志

2013-09-22 14:57:19

AtWood

2020-09-23 10:00:26

Redis數(shù)據(jù)庫命令

2019-06-25 10:32:19

UDP編程通信

2017-01-10 08:48:21

2024-02-21 21:14:20

編程語言開發(fā)Golang

2025-05-06 00:43:00

MySQL日志文件MIXED 3

2017-08-15 13:05:58

Serverless架構(gòu)開發(fā)運(yùn)維

2025-06-05 05:51:33

2015-11-04 09:57:18

JavaScript原型

2013-06-14 09:27:51

Express.jsJavaScript

2021-04-20 23:25:16

執(zhí)行函數(shù)變量

2011-04-11 16:48:12

Solaris權(quán)限

2024-03-12 00:00:00

Sora技術(shù)數(shù)據(jù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 日韩精品在线观看视频 | 五月婷婷激情网 | 国产精品久久精品 | 91精品久久久久 | 国产在线拍偷自揄拍视频 | 黄片毛片在线观看 | 久久丝袜视频 | 亚洲成人一区二区 | 精品国产91亚洲一区二区三区www | 国产成人99久久亚洲综合精品 | 国产精品高潮呻吟久久久久 | 欧美日韩一区二区三区四区 | 久久国内精品 | 精品国产乱码久久久久久蜜臀 | 日韩精品视频网 | 欧美日韩黄 | 一区二区三区四区在线 | 日韩中文在线视频 | 久久久久久国产精品 | 日韩在线不卡视频 | 日韩视频1 | 黄视频国产 | 亚洲欧美日韩在线 | 狠狠干av | 国产97在线 | 日韩 | 91精品久久久久久久久 | 日韩有码一区 | 国产一级在线观看 | 欧美性受| 欧美日韩在线观看视频 | 日韩精品一区二区三区四区视频 | 国产精久久久久久久 | 天天操天天射天天 | 亚洲精品自在在线观看 | 久久久夜 | 久久久久国产精品一区二区 | 天天操狠狠操 | 久久精品性视频 | 亚洲人成人一区二区在线观看 | 一区二区电影 | 亚洲女人的天堂 |