设计模式(11)代理模式The Proxy Pattern - 1 - 远程代理rmi
代理模式
假設(shè)現(xiàn)在需要設(shè)置一個(gè)監(jiān)視器Monitor,能夠監(jiān)視糖販賣機(jī)GumballMachine的狀態(tài),位置信息、還有多少糖果等。一個(gè)簡(jiǎn)單的想法就是創(chuàng)建一個(gè)Monitor的類,構(gòu)造參數(shù)包括GumballMachine。然后就可以調(diào)用GumballMachine的一些狀態(tài)函數(shù)。
但是如果GumballMachine在另一臺(tái)機(jī)器上、或者在不同的Java堆上,顯然是不能夠?qū)umballMachine的引用傳遞過來。
這是后就需要使用代理模式來解決。
思路:添加一個(gè)新的類叫做MonitorProxy。Monitor和MonitorProxy在一個(gè)Java堆上,MonitorProxy這個(gè)代理負(fù)責(zé)與遠(yuǎn)程的GumballMachine進(jìn)行溝通,得到數(shù)據(jù)后把自己偽裝成GumballMachine來被MonitorProxy調(diào)用。
遠(yuǎn)程方法
原理
我們要這設(shè)計(jì)這樣一個(gè)系統(tǒng):該系統(tǒng)允許我們調(diào)用本地的一個(gè)對(duì)象,本地的這個(gè)對(duì)象又能夠?qū)⒄?qǐng)求轉(zhuǎn)發(fā)到遠(yuǎn)程對(duì)象上。
四個(gè)關(guān)鍵的角色 client 、client helper、service helper、Service
客戶端
client helper的作用是讓client調(diào)用和調(diào)用遠(yuǎn)程remoter效果一樣。client helper處理轉(zhuǎn)發(fā)了client的請(qǐng)求。
換句話說,從client的角度來看,client就是在調(diào)用一個(gè)遠(yuǎn)程的服務(wù)。client helper假裝自己是service
client helper有和service一樣的方法。但是他們的邏輯不同,client helper里面的方法是對(duì)service進(jìn)行響應(yīng)請(qǐng)求、然后進(jìn)行參數(shù)轉(zhuǎn)換返回給client的。
服務(wù)器端
同客戶端一樣,存在一個(gè)Service helper負(fù)責(zé)處理對(duì)話(可能是Socket)
Java RMI(Remote Method Invocation)
Java RMI已經(jīng)封裝好了相關(guān)功能。不需要自己動(dòng)手寫網(wǎng)絡(luò)和I/O相關(guān)的代碼了。和調(diào)用本地的對(duì)象一樣。唯一的區(qū)別時(shí)會(huì)有網(wǎng)絡(luò)通訊,可能引起失敗拋出異常。
client helper 叫做RMI Stub
service helper 叫做RMI Skeleton
使用RMI
Remote service
一共五步走:
1. 寫一個(gè)Remote 接口
這個(gè)遠(yuǎn)程接口所定義的方法是client遠(yuǎn)程調(diào)用的。這也就意味著stub和實(shí)際的service都要實(shí)現(xiàn)這個(gè)接口
//1. 擴(kuò)展java.rmi.Remote接口 public interface MyRemote extends Remote{//2. 接口中所有的函數(shù)都要能夠拋出RemoteException異常//因?yàn)樯婕暗骄W(wǎng)絡(luò)或者I/O,可能出現(xiàn)連接錯(cuò)誤等。public String sayHello() throws RemoteException;//3. 要保證參數(shù)和返回值都是基本的數(shù)據(jù)類型或者是可序列化的。 }2. 實(shí)現(xiàn)Remote接口
遠(yuǎn)程的class要實(shí)現(xiàn)上一步定義的接口
// 1. 注意MyRemoteImpl所繼承的類UnicastRemoteObject, 這個(gè)類定義了一些遠(yuǎn)程服務(wù)所需要的功能函數(shù)。 public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{// 2. 拋出這個(gè)異常的原因是它的父類可能UnicastRemoteObject會(huì)拋出這個(gè)異常,所以子類也一樣。public MyRemoteImpl() throws RemoteException{}public String sayHello() {return "server says, hi";// other impl}// 主函數(shù)入口。方便起見直接寫在了這個(gè)類public static void main (String[] args) {/*方法一*/// 需要運(yùn)行第四步命令try {MyRemote service = new MyRemoteImpl();Naming.rebind(“RemoteHello”, service); //在RMI中注冊(cè)} catch(Exception ex) {ex.printStackTrace();}}/*方法二*///使用內(nèi)在注冊(cè)器,不用單獨(dú)運(yùn)行rmiregistry服務(wù)。try {//默認(rèn)端口1099Registry registry = LocateRegistry.createRegistry(1099);MyRemoteImpl service = new MyRemoteImpl();registry.bind("RemoteHello", service); // 在RMI中注冊(cè)System.out.println("Run Successfully");} catch (Exception ex) {ex.printStackTrace();} }定義好service,需要使用。方法就是把上面的MyRemoteImpl實(shí)例化之后,在RMI 中注冊(cè)這個(gè)服務(wù)。
try{
MyRemote service = new MyRemoteImpl();
Naming.rebind(“RemoteHello”, service); //起一個(gè)名字并且注冊(cè)。
}catch(Exception ex){
…
}
3. 生成stubs和skeletons(即service helper和client helper)(java1.5及以后不再被需要)可以略過。
使用rmic工具生成兩個(gè)類:
rmic MyRemoteImpl- MyRemoteImpl_Stub.class 給client
- MyRemoteImpl_Skel.class 給service
4. 運(yùn)行RMI registry(在service主機(jī)上)
rmiregistry確保運(yùn)行的文件能夠到達(dá)你的類。穩(wěn)妥的辦法是從你的類文件運(yùn)行此命令。
例如從全限定類名的根目錄開始。比如bin/test/MyRemoteImpl.class
那么從bin目錄運(yùn)行
5. 運(yùn)行遠(yuǎn)程服務(wù)(在service主機(jī)上)
java MyRemoteImplxxxx是main函數(shù)的遠(yuǎn)程服務(wù)。
從全限定類名的根目錄開始。同上。
Client
client如何能夠得到stub object呢?
import java.rmi.*; public class MyRemoteClient {// 主程序入口public static void main (String[] args) {new MyRemoteClient().go();}public void go() {try {//查找,返回時(shí)Object,需要Cast//如果是默認(rèn)端口和本地地址,可以忽略不寫MyRemote service = (MyRemote) Naming.lookup(“rmi://127.0.0.1:1099/RemoteHello”);String s = service.sayHello();System.out.println(s);} catch(Exception ex) {ex.printStackTrace();}} }client如何獲得stub這個(gè)類?
- 方法一:手工交付
- 方法二:動(dòng)態(tài)下載。放到服務(wù)器上動(dòng)態(tài),如果本地搜索不到就是用http請(qǐng)求下來。
注意事項(xiàng)
- 在遠(yuǎn)程服務(wù)啟動(dòng)之前,啟動(dòng)rmiregistry。service運(yùn)行時(shí)調(diào)用了Naming.rebind(),這個(gè)需要rmiregistry服務(wù)。所以必須提前運(yùn)行
- 忘記使參數(shù)或者返回類型序列化。這個(gè)編譯器檢查不出來。只有運(yùn)行時(shí)報(bào)錯(cuò)。
- 忘記把stub給client。
代理模式定義
為另一個(gè)對(duì)象提供一個(gè)代理或者占位符,來控制對(duì)他的訪問。
幾個(gè)控制訪問的方法:
-
一個(gè)遠(yuǎn)程代理控制對(duì)遠(yuǎn)程對(duì)象的訪問
-
虛擬代理控制對(duì)創(chuàng)建成本較高的資源的訪問
-
保護(hù)代理控制對(duì)于權(quán)限資源的訪問
遠(yuǎn)程代理
上面提的那種
虛擬代理
虛擬代理用來標(biāo)識(shí)一個(gè)需要昂貴代價(jià)去創(chuàng)建的對(duì)象。虛擬代理通常來推遲這個(gè)對(duì)象的創(chuàng)建,直到這個(gè)對(duì)象被需要時(shí)再創(chuàng)建。在被代理對(duì)象創(chuàng)建完成之前,虛擬代理扮演者被代理者(RealSubject)的角色, 創(chuàng)建完成之后,虛擬代理把請(qǐng)求轉(zhuǎn)交給被代理對(duì)象。
例如:播放在線音樂,當(dāng)音樂封面沒有被下載的時(shí)候,虛擬代理充當(dāng)封面的角色(可以使一串字符串“正在下載封面”,或者其他的某個(gè)默認(rèn)圖片), 一旦封面下載完成,虛擬代理就把所有調(diào)用的方法委托給實(shí)際的圖片。
// Head Fist Design Pattern P456 class ImageProxy implements Icon {ImageIcon imageIcon;URL imageURL;Thread retrievalThread;boolean retrieving = false;public ImageProxy(URL url) { imageURL = url; }public int getIconWidth() {if (imageIcon != null) {return imageIcon.getIconWidth();} else {return 800;}}public int getIconHeight() {if (imageIcon != null) {return imageIcon.getIconHeight();} else {return 600;}}public void paintIcon(final Component c, Graphics g, int x, int y) {if (imageIcon != null) {imageIcon.paintIcon(c, g, x, y);} else {g.drawString(“Loading CD cover, please wait...”, x+300, y+190);if (!retrieving) {retrieving = true;retrievalThread = new Thread(new Runnable() {public void run() {try {imageIcon = new ImageIcon(imageURL, “CD Cover”);c.repaint();} catch (Exception e) {e.printStackTrace();}}});retrievalThread.start();}}} }總結(jié)
以上是生活随笔為你收集整理的设计模式(11)代理模式The Proxy Pattern - 1 - 远程代理rmi的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年中国按钮开关市场趋势报告、技术
- 下一篇: 用simple mapi 发送一个带附件