初探設計模式——代理模式(遠程代理,虛擬代理,動態代理)

代理模式用來控制和訪問管理,方式有很多。代理以通過Internet對他們的代理對象搬運的整個方法調用而出名,也可以代替某些懶惰的對象做一些事情。

遠程代理

遠程方法調用流程

  1. 客戶對象調用客戶輔助對象(Proxy)的方法
  2. 客戶輔助對象打包調用信息,(lookup service)再然後通過網絡將它運給服務輔助對象
  3. 服務輔助對象(Skel)接收請求(透過Socket連接)解包,調用服務對象的相應方法
  4. 服務對象上的方法被調用,將結果返回給服務輔助對象
  5. 服務輔助對象將返回信息打包序列化通過網絡返回(Socket輸出流)到客戶輔助對象
  6. 客戶輔助對象把返回值解包反序列化返回給客戶對象

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決定的)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章