jdk動態代理

? 簡單的說,代理模式是在目標對象和訪問對象之間增加了一層代理對象,所有的訪問對象通過代理對象來實現對目標對象的調用。

????????代理對象和目標對象實現同一個接口,由代理對象來調用目標對象,對于外部來說,代理對象就可以替代目標對象被調用。通過這種方式,代理對象中可以在正常的訪問中增加額外的邏輯,比如緩存、權限控制、日志記錄等。

????????但是這種靜態代理的模式需要增加額外的代理類的實現,Java 5開始引入了動態代理機制,實現了在運行時動態地創建出代理對象,這其實是一種方法調用的攔截,AOP就是利用了這種模式。

?

動態代理的使用例子?

1
<span style="font-size:16px;">package?com.dynamic.jdk;<br><br>/**<br> * 目標類的接口定義<br> * <p/><br> * Created by Vincent Tse on 12/2/15.<br> */<br>public?interface?MyInterface {<br> ? ?void?doSomething();<br>}<br></span>
1
<span style="font-size:16px;">package?com.dynamic.jdk;<br><br>/**<br> * 目標類的具體實現<br> * <p/><br> * Created by Vincent Tse on 12/2/15.<br> */<br>public?class?MyInterfaceImpl?implements?MyInterface {<br><br> ? ?@Override<br> ? ?public?void?doSomething() {<br> ? ? ? ?System.out.println("here is my real operation!");<br> ? ?}<br>}<br></span>
1
<span style="font-size:16px;">package?com.dynamic.jdk;<br><br>import?java.lang.reflect.InvocationHandler;<br>import?java.lang.reflect.Method;<br><br>/**<br> * 自定義的InvocationHandler<br> * 封裝具體的調用過程<br> * <p/><br> * Created by Vincent Tse on 12/2/15.<br> */<br>public?class?MyInvocationHandler?implements?InvocationHandler {<br> ? ?//target是真正執行方法的目標對象<br> ? ?private Object target;<br><br> ? ?public MyInvocationHandler(Object target) {<br> ? ? ? ?super();<br> ? ? ? ?this.target = target;<br> ? ?}<br><br> ? ?/**<br> ? ? * 代理對象調用的方法<br> ? ? * @param proxy<br> ? ? * @param method<br> ? ? * @param args<br> ? ? * @return<br> ? ? * @throws Throwable<br> ? ? */<br> ? ?@Override<br> ? ?public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {<br> ? ? ? ?System.out.println("before target's operation!");<br> ? ? ? ?Object result = method.invoke(target, args);<br> ? ? ? ?System.out.println("after target's operation");<br> ? ? ? ?return result;<br> ? ?}<br>}<br></span>
1
<span style="font-size:16px;">package?com.dynamic.jdk;<br><br>import?java.lang.reflect.Proxy;<br><br>/**<br> * 測試類<br> * <p/><br> * Created by Vincent on 12/2/15.<br> */<br>public?class?DynamicTest {<br><br> ? ?public?static?void?main(String[] args) {<br><br> ? ? ? ?//生成目標對象<br> ? ? ? ?MyInterface myInterface = new MyInterfaceImpl();<br><br> ? ? ? ?//實例化invocationHandler對象,傳入目標對象作為target<br> ? ? ? ?MyInvocationHandler invocationHandler = new MyInvocationHandler(myInterface);<br><br> ? ? ? ?//調用Proxy的方法生成代理對象<br> ? ? ? ?MyInterface proxy = (MyInterface)Proxy.newProxyInstance(myInterface.getClass().getClassLoader(),<br> ? ? ? ? ? ? ? ?new Class[]{MyInterface.class}, invocationHandler);<br><br> ? ? ? ?//調用代理對象的方法<br> ? ? ? ?proxy.doSomething();<br> ? ?}<br>}<br></span>

輸出結果:

before?target's?operation!

here?is?my?real?operation!

after?target's?operation!?

?

使用起來很簡單,接下來看源碼分析其中的原理

從Proxy的newProxyInstance()方法開始,這個方法就是用來生成代理對象的,需要傳入類加載器、實現的接口以及一個InvocationHandler對象。?

1
<span style="font-size:16px;">public?static?Object newProxyInstance(ClassLoader loader,<br> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Class<?>[] interfaces,<br> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?InvocationHandler h)<br> ? ?throws?IllegalArgumentException<br>{<br> ? ?Objects.requireNonNull(h);<br><br> ? ?final?Class<?>[] intfs = interfaces.clone();<br> ? ?final?SecurityManager sm = System.getSecurityManager();<br> ? ?if?(sm !=?null) {<br> ? ? ? ?checkProxyAccess(Reflection.getCallerClass(), loader, intfs);<br> ? ?}<br><br> ? ?//通過傳入的類加載器和實現接口生成代理類<br> ? ?Class<?> cl = getProxyClass0(loader, intfs);<br><br> ? ?/*<br> ? ? * Invoke its constructor with the designated invocation handler.<br> ? ? */<br> ? ?try {<br> ? ? ? ?if (sm != null) {<br> ? ? ? ? ? ?checkNewProxyPermission(Reflection.getCallerClass(), cl);<br> ? ? ? ?}<br> ? ? ? ? ? ? ? ? ?//反射獲取代理類的構造函數<br> ? ? ? ?final Constructor<?> cons = cl.getConstructor(constructorParams);<br> ? ? ? ?final InvocationHandler ih = h;<br> ? ? ? ?if (!Modifier.isPublic(cl.getModifiers())) {<br> ? ? ? ? ? ?AccessController.doPrivileged(new PrivilegedAction<Void>() {<br> ? ? ? ? ? ? ? ?public Void run() {<br> ? ? ? ? ? ? ? ? ? ?cons.setAccessible(true);<br> ? ? ? ? ? ? ? ? ? ?return null;<br> ? ? ? ? ? ? ? ?}<br> ? ? ? ? ? ?});<br> ? ? ? ?}<br> ? ? ? ?//反射生成實例,將invocationHandler傳入,之后調用invoke方法就靠它了<br> ? ? ? ?return cons.newInstance(new Object[]{h});<br> ? ?} catch (IllegalAccessException|InstantiationException e) {<br> ? ? ? ?throw new InternalError(e.toString(), e);<br> ? ?} catch (InvocationTargetException e) {<br> ? ? ? ?Throwable t = e.getCause();<br> ? ? ? ?if (t instanceof RuntimeException) {<br> ? ? ? ? ? ?throw (RuntimeException) t;<br> ? ? ? ?} else {<br> ? ? ? ? ? ?throw new InternalError(t.toString(), t);<br> ? ? ? ?}<br> ? ?} catch (NoSuchMethodException e) {<br> ? ? ? ?throw new InternalError(e.toString(), e);<br> ? ?}<br>} <br></span>

?

?

getProxyClass0()方法就是用來生成代理類的,首先檢查實現接口數量,大于65535就拋異常,記得以前java培訓的時候老師講過這塊,感覺應該不會有實現那么多接口的類吧。

1
<span style="font-size:16px;">private?static?Class<?> getProxyClass0(ClassLoader loader,<br> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Class<?>... interfaces) {<br> ? ?if?(interfaces.length >?65535) {<br> ? ? ? ?throw?new?IllegalArgumentException("interface limit exceeded");<br> ? ?}<br><br> ? ? ??//從一個Cache中獲取代理類,如果沒有就重新生成<br> ? ?return proxyClassCache.get(loader, interfaces);<br>}<br></span>

?

proxyClassCache是一個靜態的WeakCache,定義在Proxy類里。

WeakCache里有兩個factory,一個是subKeyFactory,?是一個映射函數(key, parameter)->sub-key, 另一個是valueFactory,是一個映射函數(key,?parameter)->value。WeakCache的get方法需要兩個參數,一個是key,另一個是parameter。

具體WeakCache的用法與原理這里不再贅述,請參考源碼。

1
<span style="font-size:16px;">private?static?final?WeakCache<ClassLoader, Class<?>[], Class<?>><br> ? ?proxyClassCache =?new?WeakCache<>(new?KeyFactory(),?new?ProxyClassFactory());<br></span>

這里傳入的KeyFactory和ProxyClassFactory是Proxy類中定義的靜態類,分別對應了WeakCache中的subKeyFactory和valueFactory,都實現了BiFunction接口,并實現了apply()方法。ProxyClassFactory的apply()方法里完成的就是生成代理類的邏輯,最關鍵的是

1
<span style="font-size:16px;">byte[] proxyClassFile = ProxyGenerator.generateProxyClass(<br> ? ?proxyName, interfaces, accessFlags);<br></span>

上面的方法用來生成代理類的字節碼,這個代理類里會有接口的實現方法,在實現的方法中會調用InvocationHandler的invoke()方法,有興趣的可以將生成的字節碼寫到本地,用反編譯工具打開看一下。

?

那么ProxyClassFactory的apply()方法是在什么時候被調用的呢,回到WeakCache的get()方法

1
<span style="font-size:16px;">//生成subkey來獲取對應的supplier<br>Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));<br>Supplier<V> supplier = valuesMap.get(subKey);<br>Factory factory = null;<br>//一直循環,直到從supplier中獲取到value為止<br>//如果沒有supplier就生成factory并加入到緩存中(源碼說這是一個install的過程)<br>while (true) {<br> ? ?if (supplier != null) {<br> ? ? ? ?V value = supplier.get();<br> ? ? ? ?if (value != null) {<br> ? ? ? ? ? ?return value;<br> ? ? ? ?}<br> ? ?}<br> ? ?<br> ? ?if (factory == null) {<br> ? ? ? ?//Factory是內部類,實現了Supplier接口,實現了get()方法<br> ? ? ? ?//實際上在get()方法中調用了valueFactory(也就是ProxyClassFactory)的apply()方法<br> ? ? ? ?//可以debug進去看<br> ? ? ? ?factory = new Factory(key, parameter, subKey, valuesMap);<br> ? ?}<br><br> ? ?if (supplier == null) {<br> ? ? ? ?supplier = valuesMap.putIfAbsent(subKey, factory);<br> ? ? ? ?if (supplier == null) {<br> ? ? ? ? ? ?supplier = factory;<br> ? ? ? ?}<br> ? ?} else {<br> ? ? ? ?if (valuesMap.replace(subKey, supplier, factory)) {<br> ? ? ? ? ? ?supplier = factory;<br> ? ? ? ?} else {<br> ? ? ? ? ? ?supplier = valuesMap.get(subKey);<br> ? ? ? ?}<br> ? ?}<br>}<br></span>

好辛苦,終于生成代理類了,回到newProxyInstance方法,有了代理類的Class對象,就可以通過反射生成實例(調用的是帶InvocationHandler參數的構造函數),生成實例之后就可以用代理對象了。

轉載于:https://www.cnblogs.com/jinshiyill/p/5032139.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/256294.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/256294.shtml
英文地址,請注明出處:http://en.pswp.cn/news/256294.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【圖像處理】——圖像的灰度化處理(Python實現三種方法——最大值法、平均值法、加權均值法、gamma校正)

目錄 一、什么是圖像的灰度化? 二、灰度化的幾種方法(最大值法、平均值法、加權均值法、gamma校正)

配置https

引子&#xff1a; 最近在一篇文章中了解到EFF(電子前哨基金會)為了推廣https協議&#xff0c;成立了一個letsencrypt項目&#xff0c;可以發放免費的證書&#xff0c;此證書可以被大多數主流瀏覽器所信任&#xff0c;這個邪惡的念頭一爆發&#xff0c;就讓我走上了一條坎坷的不…

C# —— 序列化與反序列化

概念 序列化 通過使用不同的類(BinaryFormatter,SoapFormatter,XmlSerializer)將對象狀態轉換為可保持或傳輸的格式的過程,具體是將對象轉變為字節流,其目的是為了保存數據的狀態,方便后續還原調用。包括三種序列化形式:二進制序列化,SOAP序列化,XML序列化。于此過…

CentOS 6.5安裝VNC server

1. 安裝桌面&#xff0c;安裝時選擇了Desktop可以忽略 # yum groupinstall Desktop # yum install gnome-core xfce4 firefox 2. 安裝VNC server # yum install tigervnc-server 3. 配置服務 # chkconfig vncserver on 4. 設置VNC用戶密碼 # vncpasswd 5. 配置文件 # vi /etc/s…

【圖像處理】——圖像灰度直方圖的繪制(直接調用函數和自定義函數)

目錄 一、灰度直方圖概念 二、直接調用opencv的函數caclHist() 1、函數介紹 2、實例 <

Codeforces 722C. Destroying Array

C. Destroying Arraytime limit per test1 secondmemory limit per test256 megabytesinputstandard inputoutputstandard outputYou are given an array consisting of n non-negative integers a1,?a2,?...,?an. You are going to destroy integers in the array one by o…

C#中數據類型及其轉換知識點匯總

概念 C#中數據類型分為兩大類&#xff0c;分別是值類型和引用類型。 值類型變量是從類 System.ValueType 中派生出來的&#xff0c;當聲明一個值類型變量時&#xff0c;系統分配內存來存儲值。 整形 包括8種類型&#xff0c;區別在于字節數和有無符號。 浮點型 float占用…

10億個字符串的排序問題

一、問題描述 有一個大文件&#xff0c;里面有十億個字符串&#xff0c;亂序的&#xff0c;要求將這些字符串以字典的順序排好序 二、解決思路 將大文件切割成小文件&#xff0c;每個小文件內歸并排序&#xff1b; 對所有的小文件進行歸并排序——多重歸并排序 三、解決方案 3.…

MVC學習IIS的不同版本(一)

一&#xff1a;IIS5.0運行在進程InetInfo.exe中&#xff0c;該進程寄宿著一個名為World Wide Publishing Service&#xff08;W3VC&#xff09;的window服務。 W3VC的主要功能&#xff1a;包括HTTP請求的監聽、工作進程和配置管理 檢測到HTTP 請求時&#xff1a; 根據擴展名判斷…

Halcon中visualize_object_model_3d算子詳解

概念 此函數用于顯示3d模型。該函數功能很多,包括設置位姿,顏色,鼠標翻轉、縮放、平移,選擇和取消選擇目標,降低鼠標靈敏度,切換檢查模式等。 參數 visualize_object_model_3d( : : WindowHandle, ObjectModel3D, CamParam, PoseIn, GenParamName, GenParamValue, Tit…

random()模塊隨機函數的用法總結

random()是Python中生成隨機數的函數&#xff0c;是由random模塊控制&#xff0c;random()函數不能直接訪問&#xff0c;需要導入random 模塊&#xff0c;然后再通過相應的靜態對象調用該方法才能實現相應的功能 目錄 1. random.random() 2. random.uniform() 3. random.ra…

ansible命令應用示例

ansible命令應用示例 ping slave組ansible slave -m ping 用bruce 用戶以root 身份pingansible slave -m ping -u bruce --sudo 用bruce 用戶sudo 到batman 用戶pingansible slave -m ping -u bruce --sudo --sudo-user batman 給slave組安裝ftpan…

史上超全halcon常見3D算子匯總(一)

讀取3D模型 read_object_model_3d 此算子用于讀取3D對象。 read_object_model_3d( : : FileName, Scale, GenParamName, GenParamValue : ObjectModel3D, Status) FileName:文件名,halcon支持多種3d數據格式的讀取,包括 .off, .ply, .dxf, .om3, .obj, .stl等格式。 1).…

Python:常用模塊簡介(1)

sys模塊 >>> sys.platform #返回操作系統平臺名稱 win32 >>> sys.stdin #輸入相關 <open file <stdin>, mode r at 0x000000000337B030> >>> sys.stdout #輸出相關 <open file <stdout>, mode w at 0x000000000337…

【圖像處理】——Python實現圖像加噪(隨機噪聲、椒鹽噪聲、高斯噪聲等)

目錄 1、隨機噪聲 2、椒鹽噪聲 3、高斯噪聲 補充:numpy.clip函數 4、其他噪聲 1、隨機噪聲 隨機噪聲就是通過隨機函數在圖像上隨機地

100NED

將生活融入編程轉載于:https://www.cnblogs.com/nedhome/p/5036680.html

Windows10 VS2019下使用CMake3.20.1打開PCL1.11.0程序

安裝CMake 為什么要使用cmake cmake 是kitware 公司以及一些開源開發者在開發幾個工具套件(VTK)的過程中衍生品&#xff0c;成為一個獨立的開放源代碼項目。 CMake是一個很強大的編譯配置工具&#xff0c;支持多種平臺和編譯器&#xff0c;通過編寫CMakeLists.txt&#xff0c…

Java 并發---ConcurrentHashMap

concurrent包下的并發容器 JDK5中添加了新的concurrent包&#xff0c;相對同步容器而言&#xff0c;并發容器通過一些機制改進了并發性能。因為同步容器將所有對容器狀態的訪問都串行化了&#xff0c;這樣保證了線程的安全性&#xff0c;所以這種方法的代價就是嚴重降低了并發性…

【圖像處理】——圖像濾波(Python+opencv實現三種方法:均值濾波、中值濾波、高斯濾波等)

目錄 一、什么是濾波以及濾波的目的? 二、均值濾波(cv2.blur()) 1、原理 2、關鍵代碼