代理模式用來控制和訪問管理,方式有很多。代理以通過Internet對他們的代理對象搬運的整個方法調用而出名,也可以代替某些懶惰的對象做一些事情。
遠程代理
遠程方法調用流程
- 客戶對象調用客戶輔助對象(Proxy)的方法
- 客戶輔助對象打包調用信息,(lookup service)再然後通過網絡將它運給服務輔助對象
- 服務輔助對象(Skel)接收請求(透過Socket連接)解包,調用服務對象的相應方法
- 服務對象上的方法被調用,將結果返回給服務輔助對象
- 服務輔助對象將返回信息打包序列化通過網絡返回(Socket輸出流)到客戶輔助對象
- 客戶輔助對象把返回值解包反序列化返回給客戶對象
RMI提供了客戶輔助對象(STUB)和服務輔助對象(SKELETON),在java5後可直接由動態代理產生,爲客戶輔助對象提供和服務對象相同的方法,讓我們在客戶端和服務端建立聯繫。
服務端實現
製作遠程接口
import java.rmi.*;
public interface MyRemote extends Remote{ //擴展Remote接口表示MyRemote支持遠程操作
public String sayHello()throws RemoteException; //客戶調用實現此接口的Stub上的方法,通過處理/聲明遠程異常來解決在網絡/IO上出現的問題
} //確定變量和返回值是Primitive類型或者是可序列化(類實現Serializable接口,transient)類型,傳到客戶端後String接收
製作遠程服務實現,實現了接口,是實際工作的類
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{ //通過超類實現一些遠程功能
public String sayHello(){
return "hey";
}
public MyRemoteImpl() throws RemoteException{} //超類構造器會拋出RemoteException,聲明子類的構造器也拋出異常
//用RMI Registry註冊此服務
public static void main(String[] args){
try{
MyRemote service=new MyRemoteImpl(); //生成遠程服務對象
Naming.rebind("RemoteHello",service) //在RMI Registry註冊此名字和服務,綁定服務對象時RMI會把服務換成stub放到registry中
}catch(Exception ex){
ex.printStackTrace();
}
}
}
爲一個服務類產生Stub(客戶lookup時返回對象到客戶端)和Skeleton:在遠程實現類上執行rmic
開啓一個終端啓動啓動rmiregistry,客戶可以從中查到代理位置(客戶的stub helper對象)
開啓另一個終端啓動服務
客戶端實現:獲取代理,調用方法
import java.rmi.*;
public class MyRemoteClient{
public static void main(String[] args){
new MyRemoteClient().go();
}
public void go(){
try{
//lookup方法可以獲取客戶所需的stub對象(代理)
MyRemote service=(MyRemote)Naming.lookup(rmi://127.0.0.1/RemoteHello);//使用遠程接口作爲服務類型;返回值類型是Object,轉換
String s=service.sayHello(); //將返回的的字節序列反序列化重新構建成String類型對象
Syetem.out.print(s);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
//動態類下載
代理模式:爲另一個對象提供一個替身或佔位符以控制對這個對象的訪問。
代理控制的方式:
遠程代理控制訪問遠程對象(遠程代理)
虛擬代理控制訪問創建開銷大的資源(虛擬代理)
保護代理基於權限控制對資源的訪問(保護,動態代理)
動態代理,一種根據訪問權限
java.lang.reflect支持在運行時動態創建一個代理類,實現一個或多個接口,並將方法的調用轉發到指定的類。
- Subject接口,實現了這個接口的RealSubject類和Proxy代理類
InvocationHandler接口以及實現它的代理類InHandler
Proxy由java直接實現,任何的方法調用會轉到InHandler類中
InHandlerler類控制對RealSubject方法的訪問並響應代理的任何調度
創建InHandler(調用處理器)
import java.lang.reflect.*;
public class InHander implements InvocationHandler{ //實現InvocationHandler接口
Subject realSubject;
public InHander(Subject realSubject){ //將真正主題傳入構造器,保持引用
this.realSubject=realSubject;
}
public Object invoke(Object proxy,Method method,Object[] args) //每次proxy的方法被調用就會導致proxy調用此方法
throws IllegalAccessException{
try{
if(method.geyName().startWith("get")){
return method.onvoke(realSubject,args); //將請求轉到真是服務對象
}else if(method.getName().equals("sada")){
throw new IllegalAcessException();
}
}catch(InvocationTargetException e){ //當真正主題拋出異常就會執行這裏
e.printStackTrace();
}
return null;
}
}
創建Proxy類並實例化
Subject getProxy(Subject realSubject){ //代理和主題的共同接口
return(Subject)Proxy.newProxyInstance( //利用靜態的newProxyInstance方法創建代理,傳入接口數組
realSubject.getClass().getClassLoader(), //Subject的類載入器作參數
realSubject.getClass().getInterfaces(), //代理需要實現的接口
new InHandler(realSubject)); //將主題傳入調用處理器,這樣調用處理器就可以訪問主題
}
動態代理在運行時根據傳入的接口集創建proxy類,proxy會把調用請求傳給InvocationHandler,由調用處理器決定請求是否傳到主題(調用處理器中主題的引入也是有proxy決定的)