Java中動態代理的實作教程
以下的內容部分參考了網路上的內容,在此對原作者表示感謝!
Java中動態代理程式的實現,關鍵是這兩個東西:Proxy、InvocationHandler,以下從InvocationHandler介面中的invoke方法入手,簡單說明Java如何實現動態代理的。
首先,invoke方法的完整形式如下:
- ##Java程式碼
-
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable #112 {
## return
null;
- # 先猜測一下,method是呼叫的方法,也就是需要執行的方法;args是方法的參數; proxy,這個參數是什麼?以上invoke()方法的實作就是比較標準的形式,我們看到,這裡並沒有用到proxy參數。查看JDK文件中Proxy的說明,如下:
Java程式碼
A method invocation on a proxy instance through one of its proxy interfaces will be dispatchedo proxy instance, a java.lang.reflect.Method object identifying the method that was invoked, and an array of type Object containing the . 因此可以知道以上的猜測是正確的,同時也知道,proxy參數傳遞的即是代理類別的實例。 為了方便說明,這裡寫一個簡單的範例來實作動態代理。
-
#Java代碼
-
Java代碼
//抽象角色(動態代理只能代理介面)
public - interface Subject {
public void request();
}
//真實角色:實作了Subject的request()方法
public #class RealSubject implements Subject{
public void request(){
System.out.println("From real subject.");
}
}
-
Java程式碼
-
//實作了InvocationHandler
- public
class DynamicSubject
#implements -
##{
private Object obj;//這是動態代理的好處,被封裝的物件是Object類型,接受任意型別的物件
public DynamicSubject ()
- {
- }
- ##> ##
- public DynamicSubject(Object obj)
;
}
-
##
//這個方法不是我們所顯示的去呼叫 - public Object invoke(Object proxy, Method method, Object[] ) ##oo # {
# System.out.println("before cal# +#1method); ## method.invoke(obj, args); //----->這一步詳看下一篇文章,java 以反射機制呼叫某個類別的方法,看完你會了解的。
System.out.println(
//用戶端:產生代理實例,並且呼叫了request()方法
public
## class Client { public static #void main(String[] args) throws Throwable{
- #// TOf
## Subject rs=new RealSubject();//這裡指定被代理類
InvocationHandler ds=new DynamicSubject(rs);
## //以下為一次產生代理程式
-
-
## Subject subject=(Subject) Proxy.newProxyInstance(
## ds); //這裡可以透過運作結果證明subject是Proxy的一個實例,這個實例實現了Subject介面
## System.out.println(subject instanceof Proxy);
//這裡可以看出subject的Class類別是$Proxy0,這個$Proxy0類別繼承了Proxy,實作了Subject介面
- ## println(
"subject的Class類別是:"+subject.getClass().toString());
- "subject中的屬性有:");
- ## ] field=subject.getClass().getDeclaredFields();
for(Field f:field){
- System.out.print(f.getName()+ ", ");
- ## ## System.out.print(
- "\n"+
"subject中的方法有:");
## Method[] method=subject.getClass().getDeclaredMethods();
- for(Method m:method){
### "); ############### } ######################## ##"\n"+###"subject的父類別是:"+subject.getClass().getSuperclass()); ############################# ########## System.out.print(###"\n"+###"subject實作的介面是:"); ############################################################################################ ################# Class>[] interfaces=subject.getClass().getInterfaces(); ############## ########## ###for(Class> i:interfaces){ ################ )+###", "); ########
}
System.out. "運作結果為:");
- subject.request();
- #}
- Xml程式碼
- 運行結果如下:此處省略了包名,***取代
- #true
- subject的Class類別是:class $Proxy0
- subject中的屬性有:m1, m3, m0, m2,
- subject中的方法方法有:request, hashCode, equals, toString,
- subject的父類是:class java.lang.reflect.Proxy
- subject實現的介面是:cn.edu.ustc.dynamicproxy.Subject,
- 運作結果為:
#before calling public abstract void ***.Subject.request() From real subject.
fter void ***.Subject.request()
PS:這個結果的訊息非常重要,至少對我來說。因為我在動態代理犯暈的根源就在於將上面的subject.request()理解錯了,至少是被表面所迷惑,沒有發現這個subject和Proxy之間的聯繫,一度糾結於最後調用的這個request( )是怎麼跟invoke()聯絡上的,而invoke又是怎麼知道request存在的。其實上面的true和class $Proxy0就能解決很多的疑問,再加上下面將要說的$Proxy0的源碼,完全可以解決動態代理的疑惑了。
public static Object newProxyInstance(ClassLoader loader,
## ,
IncallingHandler h)
#拋出IllegalArgumentException
#{
if (h == null) {
- ##
new空指標異常();
} - ##
- /*
- * 尋找或產生指定的代理類別。
- */
- /*
- ##
/*
-
建構函式cons = cl.getConstructor(constructorParams);
- #return (Object) cons.newInstance(
new Object[] { h });
} catch (NoSuchMethodException e) {
# ##新的內部錯誤(e.toString());
-
}
catch (IllegalAccessException e) { # ##新的內部錯誤(e.toString());
} catch (InstantiationException e) {
##o ## ##新的內部錯誤(e.toString()); -
}
catch (InitationTargetException e) { ## ##新的內部錯誤(e.toString());
-
}
} -
##
Proxy.newProxyInstance(ClassLoader loader, Class>Class>Class> ;[] interfaces, InvocationHandler h)做了以下幾件事.
(1)根據參數loader和interfaces調用方法getProxyClass(loader, interfaces)創建代理類$Proxy0.$Profaces調用類實現了interfaces的接口,並並繼承了Proxy類別.
(2)實例化$Proxy0並在建構方法中把DynamicSubject傳過去,接著$Proxy0呼叫父類別Proxy的建構器,為h賦值,如下:
#class Proxy{
# InvocationHandler h=null;
protected Proxy(InvocationHandler h) {
= h;
- # }
- ...
- ## ... }
#'
來看看這個繼承了Proxy的$Proxy0的原始碼:public final class $Proxy0 extends Proxy implements Subject {
-
#11 # private
static 方法m1; -
private
static 方法 m0; -
private static 方法 m3;
-
private static 方法 m2; ##########靜止的{###### # try {
## m1 = Class.forName( - "java.
- ##
Fnew Class[] { Class.forName("java.lang.Object"#new Class[] { Class.forName("java.lang.Object"#new Class);
-
m0 = Class.forName(
"java.lang.Object").getMethod( "hashCode", # 地
-
m3 = Class.forName(
"***.RealSubject").getMethod( "請求", - new Class[
0]);
- m2 = Class.forName(
- "java.lang.Object").getMethod(
"toString",
-
-
}
catch (NoSuchMethodException nosuchmeth # - 以拋出
new NoSuchMethodError(nosuchmethodException.getMessage());
- }
- catch (ClassNotFoundException classnotfoundException) {
新的 NoClassDefFoundError(classnotfoundException.getMessage()); ###########//靜止的#### #
## public $Proxy0(InitationHandler incallinghandler) { ##
-
}
################## ###@Override ######## ###@Override ########## ###public ###final ###boolean equals(Object obj) { ############### ##試{ ###### return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void request() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
The Proxy0 instance is cast to Subject and the reference is assigned to subject. When the subject.request() method is executed, the request() method in the $Proxy0 class is called, and then the invoke() method of h in the parent class Proxy is called. That is, InvocationHandler.invoke().
PS: 1. One thing that needs to be explained is that the getProxyClass method in the Proxy class returns the Proxy Class class. The reason why I explain this is because I made a low-level mistake at the beginning, thinking that what was returned was the "Class of the proxied class" - -! It is recommended to take a look at the source code of getProxyClass, it is very long =. =
2. It can be seen from the source code of $Proxy0 that the dynamic proxy class not only proxies the methods in the explicitly defined interface, but also proxies the inherited equals() in the java root class Object. , hashcode(), toString(), and only these three methods.
Q: So far, there is still a question. The first parameter in the invoke method is an instance of Proxy (to be precise, the instance of $Proxy0 is ultimately used), but what? What to use? In other words, how does the program show its effect?
A: From my current level, this proxy parameter has no effect. In the entire dynamic proxy mechanism, the proxy parameter of the invoke method in InvocationHandler is not used. The parameter passed in is actually an instance of the proxy class. I think it may be to allow programmers to use reflection in the invoke method to obtain some information about the proxy class.
#
以上是Java中動態代理的實作教程的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

聚類演算法中的聚類效果評估問題,需要具體程式碼範例聚類是一種無監督學習方法,透過對資料進行聚類,將相似的樣本歸為一類。在聚類演算法中,如何評估聚類的效果是一個重要的問題。本文將介紹幾種常用的聚類效果評估指標,並給出對應的程式碼範例。一、聚類效果評估指標輪廓係數(SilhouetteCoefficient)輪廓係數是透過計算樣本的緊密度和與其他簇的分離度來評估聚類效

解決C++程式碼中出現的「error:redefinitionofclass'ClassName'」問題在C++程式設計中,我們常常會遇到各種各樣的編譯錯誤。其中一個常見的錯誤是「error:redefinitionofclass'ClassName'」(類別『ClassName』的重定義錯誤)。這個錯誤通常出現在同一個類別被定義了多次的情況下。本文將

Steam是十分受歡迎的一個平台遊戲,擁有眾多優質遊戲,可是有些win10用戶體現自己下載不了steam,這是怎麼回事呢?極有可能是用戶的ipv4伺服器位址沒有設定好。要解決這個問題的話,你可以試著在相容模式下安裝Steam,隨後手動修改一下DNS伺服器,將其改成114.114.114.114,以後應當就能下載了。 win10下載不了steam怎麼辦:WIn10下能夠試著相容模式下安裝,更新後必須關掉相容模式,不然網頁將無法載入。點擊程式安裝的屬性,以相容模式運作運行這個程式。重啟以增加內存,電

iPhone以其強大的性能和多方面的功能而聞名,它不能倖免於偶爾的打嗝或技術困難,這是複雜電子設備的共同特徵。遇到iPhone問題可能會讓人感到沮喪,但通常不需要警報。在這份綜合指南中,我們旨在揭開與iPhone使用相關的一些最常遇到的挑戰的神秘面紗。我們的逐步方法旨在幫助您解決這些常見問題,提供實用的解決方案和故障排除技巧,讓您的裝置恢復到最佳工作狀態。無論您是面對一個小故障還是更複雜的問題,本文都可以幫助您有效地解決這些問題。一般故障排除提示在深入研究具體的故障排除步驟之前,以下是一些有助於

解決PHP報錯:繼承父類別時遇到的問題在PHP中,繼承是重要的物件導向程式設計的特性。透過繼承,我們能夠重複使用現有的程式碼,並且能夠在不修改原有程式碼的情況下,對其進行擴展和改進。儘管繼承在開發中應用廣泛,但有時在繼承父類別時可能會遇到一些報錯問題,本文將圍繞解決繼承父類別時遇到的常見問題進行討論,並提供相應的程式碼範例。問題一:未找到父類別在繼承父類別的過程中,如果系統無

解決jQuery.val()無法使用的問題,需要具體程式碼範例對於前端開發者,使用jQuery是常見的操作之一。其中,使用.val()方法來取得或設定表單元素的值是非常常見的操作。然而,在一些特定的情況下,可能會出現無法使用.val()方法的問題。本文將介紹一些常見的情況以及解決方案,並提供具體的程式碼範例。問題描述在使用jQuery開發前端頁面時,有時候會碰

弱監督學習中的標籤獲取問題,需要具體程式碼範例引言:弱監督學習是一種利用弱標籤進行訓練的機器學習方法。與傳統的監督學習不同,弱監督學習只需利用較少的標籤來訓練模型,而不是每個樣本都需要有準確的標籤。然而,在弱監督學習中,如何從弱標籤中準確地獲取有用的信息是一個關鍵問題。本文將介紹弱監督學習中的標籤獲取問題,並給出具體的程式碼範例。弱監督學習中的標籤獲取問題簡介:

如何處理Linux系統中頻繁出現的伺服器負載過高問題摘要:本文介紹如何處理Linux系統中頻繁出現的伺服器負載過高問題。透過優化系統配置、調整服務資源分配、偵測問題進程和運行效能調優等方法,可以有效降低負載並提高伺服器的效能和穩定性。一、引言伺服器負載過高是Linux系統中常見的問題之一,會導致伺服器運作緩慢、回應不及時,甚至無法正常運作。面對這個問題,我
