亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

用Java動態代理來創建包裝器

2019-11-18 11:05:31
字體:
來源:轉載
供稿:網友

 


java 1.3引入了名為“動態代理類”(Dynamic PRoxy Class)的新特性,利用它可為“已知接口的實現”動態地創建包裝器(wrapper)類。1.3版本問世以前,當我首次聽說當時正在提議的動態代理類時,還以為它只是一種用來吸引人的眼球的特性。雖然把它包括到語言中是一件好事,但我卻想不出它有任何實際用處。帶著這一成見,我試著用動態代理寫了一個示例程序,卻驚奇于它的巨大威力,并當即決定把它放到我的工具箱中,以便在將來的項目中使用。此后,我不斷體驗到它的好處,它總是能用正確的方法來做你想要做的事情!

假如沒有動態代理

深入探索動態代理類之前,先來看看在某些情況下,假如沒有動態代理類會是什么樣子:

public interface Robot {
void moveTo(int x, int y);
void workOn(Project p, Tool t);
}

public class MyRobot implements Robot {
public void moveTo(int x, int y) {
// stuff happens here
}
public void workOn(Project p, Tool t) {
// optionally destrUCtive stuff happens here
}
}

上述代碼展示了一個名為Robot的接口,以及該接口的一個名為MyRobot的大致的實現。假定你現在想攔截對MyRobot類發出的方法調用(可能是為了限制一個參數的值)。

public class BuilderRobot implements Robot {
private Robot wrapped;
public BuilderRobot(Robot r) {
wrapped = r;
}
public void moveTo(int x, int y) {
wrapped.moveTo(x, y);
}
public void workOn(Project p, Tool t) {
if (t.isDestructive()) {
t = Tool.RATCHET;
}
wrapped.workOn(p, t);
}
}

一個辦法就是使用顯式的包裝器類,就像上面顯示的那樣。BuilderRobot類在其構造函數中獲取一個Robot,并攔截workOn方法,確保在任何項目中使用的工具都沒有破壞性。另外,由于BuilderRobot這一包裝器實現了Robot接口,所以凡是能夠使用一個Robot的任何地方,都能使用一個BuilderRobot實例。

對于這種包裝器風格的BuilderRobot來說,一旦你想修改或擴展Robot接口,它的缺點就會暴露無遺。為Robot接口添加一個方法,就得為BuilderRobot類添加一個包裝器方法。為Robot添加10個方法,就得為BuilderRobot添加10個方法。假如BuilderRobot、CrusherRobot、SpeedyRobot和SlowRobot都是Robot包裝器類,就必須分別為它們添加10個方法。這顯然是效率極差的一種方案。

public class BuilderRobot extends MyRobot {
public void workOn(Project p, Tool t) {
if (t.isDestructive()) {
t = Tool.RATCHET;
}
super.workOn(p, t);
}
}

上述代碼是對 BuilderRobot進行編程的另一種方式。注重BuilderRobot變成了MyRobot的一個子類。這樣可解決在第2段代碼的包裝器方案中出現的問題。也就是說,修改Robot接口不必修改BuilderRobot。但這又產生了一個新問題:只有MyRobot對象才能是BuilderRobot。而在此之前,實現了Robot接口的任何對象都可以成為一個BuilderRobot?,F在,由Java施加的“線性類出身限制”(linear class parentage restrictions)禁止我們將任意Robot(ArbitraryRobot)變成一個BuilderRobot。

動態代理也有限制

動態代理則綜合了以上兩種方案的優點。使用動態代理,你創建的包裝器類不要求為所有方法都使用顯式的包裝器,創建的子類也不要求具有嚴格的出身,兩者方法可任選一種你認為最好的。但是,動態代理仍然有一個限制。當你使用動態代理時,要包裝/擴展的對象必須實現一個接口,該接口定義了預備在包裝器中使用的所有方法。這一限制的宗旨是鼓勵良好的設計,而不是為你帶來更多的麻煩。根據經驗,每個類都至少應該實現一個接口(nonconstant接口)。良好的接口用法不僅使動態代理成為可能,還有利于程序的模塊化。

使用動態代理

下面的代碼演示了用動態代理來創建一個BuilderRobot時所必需的類。注重我們創建的這個BuilderRobotInvocationHandler類甚至根本沒有實現Robot接口。相反,它實現了java.lang.reflect.InvocationHandler,只提供了一個invoke方法。代理對象上的任何方法調用都要通過這一方法進行。觀察invoke的主體,我們發現它會檢查預備調用的方法的名稱。假如這個名稱是workOn,第二個參數就切換成一個非破壞性的工具。

然而,我們得到的仍然只是一個具有invoke方法的InvocationHandler,而不是我們真正想要的Robot對象。動態代理真正的魅力要到創建實際的Robot實例時才能反映出來。在源代碼的任何地方,我們都沒有定義一個Robot包裝器或者子類。雖然如此,我們最終仍能獲得一個動態創建的類,它通過調用BuilderRobotInvocationHandler的靜態方法createBuilderRobot中的代碼片斷,從而實現了Robot接口,并集成了Builder工具過濾器。

import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class BuilderRobotInvocationHandler implements InvocationHandler {
private Robot wrapped;
public BuilderRobotInvocationHandler(Robot r) {
wrapped = r;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ("workOn".equals(method.getName())) {
args[1] = Tool.RATCHET;
}
return method.invoke(wrapped, args);
}
public static Robot createBuilderRobot(Robot toWrap) {
return (Robot)(Proxy.newProxyInstance(Robot.class.getClassLoader(),
new Class[] {Robot.class},
new BuilderRobotInvocationHandler(toWrap)));
}
public static final void main(String[] args) {
Robot r = createBuilderRobot(new MyRobot());
r.workOn("scrap", Tool.CUTTING_TORCH);
}
}

createBuilderRobot中的代碼表面上很復雜,但它的作用其實很簡單,就是告訴Proxy類用一個指定的類加載器來動態創建一個對象,該對象要實現指定的接口(本例為Robot),并用提供的InvocationHandler來代替傳統的方法主體。結果對象在一個instanceof Robot測試中返回true,并提供了在實現了Robot接口的任何類中都能找到的方法。

有趣的是,在BuilderRobotInvocationHandler類的invoke方法中,完全不存在對Robot接口的引用。InvocationHandlers并不是它們向其提供了“代理方法實現”的接口所專用的,你完全可以寫一個InvocationHandler,并將其作為眾多代理類的后端來使用。
但在本例中,我們以構造函數參數的形式,為BuilderRobotInvocationHandler提供了RobotInterface的另一個實例。代理Robot實例上的任何方法調用最終都由BuilderRobotInvocationHandler委托給這個“包裝的”Robot。但是,雖然這是最常見的設計,但你必須了解,InvocationHandler不一定非要委托給被代理的接口的另一個實例。事實上,InvocationHandler完全能自行提供方法主體,而無需一個委托目標。

最后要注重,假如Robot接口中發生改變,那么BuilderRobotInvocationHandler中的invoke方法將反應遲鈍。例如,假定workOn方法被重命名,那么非破壞性工具陷阱會靜靜地失敗,這時的BuilderRobots就有可能造成損害。較輕易檢測、但卻不一定會造成問題的是workOn方法的重載版本。假如方法具有相同的名稱,但使用一個不同的參數列表,就可能在運行時造成一個ClassCastException或者ArrayIndexOutOfBoundsException異常。為此,以下代碼給出了一個解決方案,它能生成一個更靈活的BuilderRobotInvocationHandler。在這段代碼中,任何時候在任何方法中使用一個工具,這個工具就會被替換成一個非破壞性工具。請試著用子類化處理或者傳統的委托來進行試驗。

import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class BuilderRobotInvocationHandler implements InvocationHandler {
private Robot wrapped;
public BuilderRobotInvocationHandler(Robot r) {
wrapped = r;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Class[] paramTypes = method.getParameterTypes();
for (int i=0; i < paramTypes.length; i++) {
if (Tool.class.isAssignableFrom(paramTypes[i])) {
args[i] = Tool.RATCHET;
}
}
return method.invoke(wrapped, args);
}
public static Robot createBuilderRobot(Robot toWrap) {
return (Robot)(Proxy.newProxyInstance(Robot.class.getClassLoader(),
new Class[] {Robot.class},
new BuilderRobotInvocationHandler(toWrap)));
}
public static final void main(String[] args) {
Robot r = createBuilderRobot(new MyRobot());
r.workOn("scrap", Tool.CUTTING_TORCH);
}
}
使用建議

在大多數開發環境中,用工具來取代Robot并不是一種常見的操作。還有其他許多方式可以使用動態代理。它們提供了一個調試層,可方便地記錄一個對象上的所有方法調用的具體細節。它們可執行綁定檢查,并對方法參數進行驗證。在與遠程數據源發生沖突的前提下,甚至可用它們將備用的本地測試后端動態地交換出去。假如你采用的是良好的、由接口驅動的設計方案,我個人覺得動態代理的用處肯定要比你想象的多,最終你會嘆服于它從容解決許多問題的本事!



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
另类图片亚洲另类| 欧美一区视频在线| 日韩一级黄色av| 午夜精品一区二区三区在线视| 久久久精品在线观看| 欧美激情va永久在线播放| 日本欧美精品在线| 国产精品国产三级国产专播精品人| 亚洲伊人久久综合| 亚洲激情免费观看| 日韩电影中文字幕av| 日本久久久久久久| 亚洲已满18点击进入在线看片| 亚洲欧美激情一区| 国产成人亚洲精品| 久久91亚洲精品中文字幕奶水| 中文字幕国产亚洲| 国产在线a不卡| 亚洲一区二区免费在线| 日韩高清免费观看| 国产ts一区二区| 欧美尤物巨大精品爽| 69久久夜色精品国产7777| 久久久久www| 宅男66日本亚洲欧美视频| 国产精品盗摄久久久| 91精品国产高清自在线看超| 国产精品久久久久久亚洲影视| 亚洲第一精品夜夜躁人人躁| 亚洲性无码av在线| 日韩在线国产精品| 欧美日韩精品国产| 国产偷亚洲偷欧美偷精品| 欧美日韩一区二区免费在线观看| 亚洲精品视频在线观看视频| 亚洲国产精品久久久久久| 性亚洲最疯狂xxxx高清| 国产精品视频一| 福利视频第一区| 国产一区二区丝袜高跟鞋图片| 欧美大学生性色视频| 久久91精品国产91久久跳| 亚洲精品av在线播放| 成人欧美一区二区三区在线湿哒哒| 久久久久久91| 日本高清+成人网在线观看| 亚洲大胆人体视频| 日韩第一页在线| 91探花福利精品国产自产在线| 欧美性猛交xxxx久久久| 精品无码久久久久久国产| 日本三级韩国三级久久| 欧美精品久久久久久久久久| 欧美激情三级免费| 亚洲在线视频福利| 欧美在线视频免费播放| 亚洲精品综合久久中文字幕| 国产精品免费电影| 久久久久北条麻妃免费看| 亚洲国产精品国自产拍av秋霞| 97国产精品视频人人做人人爱| 国产成人精品久久二区二区| 欧美一区二区影院| 久久免费少妇高潮久久精品99| 国产视频久久久久久久| 亚洲视频在线观看| 国产亚洲视频在线| 成人情趣片在线观看免费| 色综合色综合久久综合频道88| 午夜精品蜜臀一区二区三区免费| 久久综合久中文字幕青草| 亚洲精品91美女久久久久久久| 久久精品99久久香蕉国产色戒| 日韩高清人体午夜| 亚洲老司机av| 成人深夜直播免费观看| 久久精品夜夜夜夜夜久久| 亚洲成人网av| 国产欧美亚洲视频| 日韩免费av一区二区| 国产精品电影在线观看| 久久99视频免费| 国产精欧美一区二区三区| 91精品国产99久久久久久| 国产在线视频欧美| 日韩中文有码在线视频| 国产suv精品一区二区三区88区| 日韩av电影手机在线观看| 久久视频这里只有精品| 久久精品国产69国产精品亚洲| 伊人久久久久久久久久| 日韩中文字幕在线观看| 国产精品久久久999| 国产精品一区二区电影| 欧美猛交ⅹxxx乱大交视频| 亚洲亚裔videos黑人hd| 国产精品久久久久久久app| 国产精品美女在线| 成人黄色在线观看| 国产精品久久久久国产a级| 亚洲色图25p| 日韩欧美国产视频| 欧美亚洲另类视频| 亚洲美女免费精品视频在线观看| 久久精品国产视频| 成人h视频在线观看播放| 久久天天躁狠狠躁夜夜爽蜜月| 久久久久成人网| 亚洲电影免费观看| 亚洲男人天堂网| 欧美大片免费观看在线观看网站推荐| 成人欧美在线视频| 96精品久久久久中文字幕| 亚洲国产精品国自产拍av秋霞| 久久久精品美女| 久久五月情影视| 国产精品香蕉av| 日韩免费电影在线观看| 国产色综合天天综合网| 国产精品久久久久久久久久久久| 国产精品无码专区在线观看| 欧美中文字幕第一页| 亚洲伊人久久大香线蕉av| 中文字幕日韩精品在线| 日韩av免费在线| 91久久精品国产| 久久五月天综合| 成人激情视频免费在线| 国产精品99久久久久久久久久久久| 亚洲乱码一区av黑人高潮| 亚洲精品白浆高清久久久久久| 亚洲色图偷窥自拍| 日韩国产精品亚洲а∨天堂免| 性欧美视频videos6一9| 国产精品免费观看在线| 亚洲成人激情图| 亚洲性无码av在线| 国产精品久久91| 国产午夜精品全部视频在线播放| 欧美在线观看一区二区三区| 精品精品国产国产自在线| 亚洲视频电影图片偷拍一区| 亚洲乱码一区av黑人高潮| 欧美综合在线观看| 亚洲精品电影网| 亚洲毛片在线观看| 欧美成人精品不卡视频在线观看| 欧美午夜激情小视频| 国产成人一区二区| 欧美性做爰毛片| 在线观看国产精品91| 欧美亚洲另类视频| 国产噜噜噜噜噜久久久久久久久| 亚洲97在线观看| 蜜臀久久99精品久久久久久宅男| 国产激情视频一区| 国产精品久久久久秋霞鲁丝| 91视频免费网站| 国内精品模特av私拍在线观看| 国语自产偷拍精品视频偷| 亚洲天堂免费视频| 亚洲最新av在线网站| 亚洲经典中文字幕| 亚洲欧美在线第一页|