一、概述 代理模式為另一個對象提供一個替身或占位符以控制對這個對象的訪問。其實就是代理就是做到類似轉發的功能,針對不同代理,轉發的具體實現不同。 二、解決問題 從原理知道他就是控制客戶對一個對象的訪問,它跟現實中的中介代理類似,只是作為代表做一些受理工作,真正執行的並不是它自己。比如買火車票,外面有 ...
一、概述
代理模式為另一個對象提供一個替身或占位符以控制對這個對象的訪問。其實就是代理就是做到類似轉發的功能,針對不同代理,轉發的具體實現不同。
二、解決問題
從原理知道他就是控制客戶對一個對象的訪問,它跟現實中的中介代理類似,只是作為代表做一些受理工作,真正執行的並不是它自己。比如買火車票,外面有很多火車票代理點,我們直接去代理點買票就好而不用跑到火車票買了(暫時不考慮網購哈)。
三、結構類圖
四、應用實例
在這個例子中,主要講解遠程代理,它可以作為另一個JVM上的本地代表。客戶端調用代理的方法,代理會利用網路把請求轉發到遠程執行,並把執行結果通過網路返回到代理,最終返回到客戶端。
下麵使用引用java RMI(Remote Method Invoke遠程方法調用)的例子講解遠程代理模式。它的的工作原理圖如下,用戶向伺服器A發起話費充值請求,伺服器A通過網路調用伺服器B的方法,伺服器B把充值結果返回到伺服器A,最後返回到用戶。
代碼實現如下,首先創建一個遠程介面
package com.jet.pattern.proxy; import java.net.MalformedURLException; import java.rmi.NotBoundException; import java.rmi.Remote; import java.rmi.RemoteException; /** * description: * 遠程介面,其繼承java的Remote介面,真正幹活對象和代理都要實現這個介面 * Created by Administrator on 2017/1/17. */ public interface MyRemote extends Remote{ // 遠程調用有風險告訴客戶端 public String request(int money) throws RemoteException, MalformedURLException, NotBoundException; }
創建遠程對象
package com.jet.pattern.proxy.impl; import com.jet.pattern.proxy.MyRemote; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.server.UnicastRemoteObject; /** * description: * 真正幹活的對象,接受代理對象的訪問 * Created by Administrator on 2017/1/17. */ // 繼承UnicastRemoteObject,讓jvm幫我們完成遠程調用 public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{ // 父類構造器拋出了異常,所以子類構造器也需要拋出異常 public MyRemoteImpl() throws RemoteException { } @Override public String request(int money) throws RemoteException { System.out.println("充值" + money + "元話費成功"); return "充值成功"; } public static void main(String[] args) { try { // 產生遠程對象 MyRemote service = new MyRemoteImpl(); // 註冊遠程訪問介面 LocateRegistry.createRegistry(8888); // 註冊遠程對象,註冊名為RemoteHello,代理訪問時指定這個名稱就可以找到本類, Naming.rebind("rmi://localhost:8888/RemoteHello",service); } catch (Exception e) { e.printStackTrace(); } } }
創建代理對象
package com.jet.pattern.proxy.impl; import com.jet.pattern.proxy.MyRemote; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; /** * description: * 代理對象,控制客戶端的訪問,把客戶端訪問請求轉發到真正幹活的對象 * Created by chenzanjin on 2017/2/4. */ public class Proxy implements MyRemote { @Override public String request(int money) throws RemoteException, MalformedURLException, NotBoundException { MyRemote remote = (MyRemote)Naming.lookup("rmi://localhost:8888/RemoteHello"); return remote.request(money); } }
創建訪問客戶端
package com.jet.pattern.proxy.test; import com.jet.pattern.proxy.MyRemote; import com.jet.pattern.proxy.impl.Proxy; import java.net.MalformedURLException; import java.rmi.NotBoundException; import java.rmi.RemoteException; /** * description: * 代理客戶端,測試對代理的訪問 * Created by chenzanjin on 2017/2/4. */ public class ProxyClient { public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException { MyRemote proxy = new Proxy(); System.out.println(proxy.request(100)); } }
測試步驟:
1、啟動MyRemoteImpl 類的main方法,綁定遠程對象到jvm。
2、啟動ProxyClient 類的main方法,對代理對象發起訪問。
代碼輸出如下:
MyRemoteImpl 類會輸出:充值100元話費成功
ProxyClient 類輸出:充值成功
五、其他代理
1、虛擬代理:可以作為創建開銷大的對象的代表,虛擬代理讓我們真正需要使用一個對象時才會去創建這個對象。在對象創建前和創建中,虛擬代理扮演真實對象的替身,當對象創建完畢後,虛擬代理會把請求直接委托給真實對象。虛擬代理在手機頁面載入中經常可以見到,比如打開頁面顯示"**正在玩命載入中..."。
2、緩存代理:可以理解成把網路上的靜態資源或者不常改變的數據保存在本地,再次訪問的這次數據的時候直接從本地讀取。應用例子有頁面的緩存機制、cdn、app緩存機制等。
3、保護代理:根據角色或者其他機制控制用戶對一些資源的訪問許可權。這常見於一些網站需要登錄才能獲取更多的內容,struts2和spring的攔截器等。
4、靜態代理:可以理解為對象由代理創建,在創建前後做一些事情,比如統計創建了多少對象,但靜態代理只能創建一個或固定的幾個對象。詳見:http://blog.csdn.net/lidatgb/article/details/8941711。
5、動態代理:其用途跟靜態代理類型,但可以利用反射機制動態地創建對象,可以減少代理代碼的重覆,有更好的擴展性。詳見:http://blog.csdn.net/lidatgb/article/details/8993131。
6、正向代理:用戶可以指定代理伺服器轉發請求到目標伺服器完成,用戶知道代理伺服器地址和目標伺服器地址。常見於FQ代理伺服器。
7、反向代理: 用戶訪問的目標伺服器其實是代理伺服器,代理伺服器做負載均衡後把請求轉發到真正的目標伺服器執行。常見於負載均衡伺服器。
六、使用場景
由代理的概述中知道,但需要控制對資源或者對象的訪問時使用代理模式。從代理的種類中可以進一步看清代理模式的使用場景,無非就是轉發遠程請求、作為創建開銷大對象的替身、在本地保存網路資源、控制一些受保護資源的許可權訪問、作負載均衡。