代理設計模式
給某一個對象提供代理對象,由代理對象控制具體對象的使用。
什麼叫代理?
在生活中,代理無處不在,如各種代理商。
如:生產電腦的廠家不會直接把電腦賣給零售客戶,而是通過代理來完成銷售。客戶也不用因爲買電腦而跑去廠家。
這就是代理過程
代理類:
主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後處理消息等。
代理類本身並不真正實現服務,而是通過調用委託類的相關方法,來提供特定的服務。
代理類與委託類都實現了同一個接口。
代理中涉及的角色:
1,抽象主題角色:聲明真實角色和代理對象的共同接口
2,真實角色:是我們最終要引用的對象
3,代理角色:代理對象內部加了一些自己的處理方法,最後還是要去調用真實角色的處理方法。
簡單代理結構圖:
爲什麼要使用代理?
1,授權機制不同,不用級別的用戶對同一對象擁有不同的訪問權限。如論壇中,註冊用戶和遊客的權限差別。
2,不讓客戶端直接操作到某個對象,但又必須和那個對象有所互動。
如:
在網絡中,在我們訪問某個遠端的服務器上的某個對象的時候,如果直接操作這個對象,網絡速度原因可能比較慢,那我們就可以先用Proxy來代替那個對象。
如果我們在加載示一個很大的圖片的時候,可以讓其在後臺加載,代理中顯示等待信息。
下面看實例:
package proxy;
//公共接口,產生須要讓其子類實現的方法
interface SellInterface
{
public void sell();
}
//真實角色,實現SellInterface
class SellFactory implements SellInterface
{
public void sell() {
System.out.println("真實角色在處理問題");
}
}
//代理類
class MyProxy implements SellInterface
{
SellFactory sf; //代理類須要調用真實角色的處理方法
public void sell()
{
/*
這樣調用,可以讓這個代理類在這裏先處理一些問題,
比如判斷什麼的,如果不滿足,就不用去調用具體實現方法了,這樣可以提高效率。
在實際應用中,如我們在訪問網站時,也是先訪問的本地服務器,如果有自己所須要的,
在本地就可以完成訪問,沒必要每次都去總部訪問資源,這樣很佔空間。
再比如:我們買正品戴爾電腦,不用去總部,在代理處就可以了。如果代理處滿足不了,
在去總部。
*/
if(test()){ //滿足條件
sf = new SellFactory();
sf.sell();
}
else
throw new RuntimeException();
}
protected boolean test()
{
return true;
}
}
//客戶端
public class CommonProxy {
public static void main(String [] args)
{
//在代理處辦事
SellInterface mp = new MyProxy();
mp.sell();
}
}
代理模式的--動態代理
JVM在執行時,運用反射機制動態創建一個類,如果這個類是代理類,那麼這樣的形式就是動態代理類。
動態代理和普通代理的區別?
普通代理類:在程序運行前,代理類的class文件已經存了
動態代理類:程序運行時 運用反射機制 動態創建而成
在上面的代理中,我們讓代理類實現了接口SellInterface,這樣使得代碼,通用性很低。
在java中提供了其它方法,使得代理類通用性提高了。
通過類Proxy 和 接口InvocationHandler 來實現對代理模式的支持。
Proxy 提供用於創建動態代理類和實例的靜態方法,它還是由這些方法創建的所有動態代理類的超類。
代理創建過程
1,首先通過Proxy類的方法來創建代理對象,須要通過來主題角色建立
2,代理類須要實現InvocationHandler 接口
動態代理結構圖
動態代理的建立:兩種方法
由Proxy類 和 接口 InvocationHandler 來共同完成。
Proxy 提供用於創建動態代理類和實例的靜態方法,它還是由這些方法創建的所有動態代理類的超類。
InvocationHandler 是代理實例的調用處理程序實現的接口.
如:建立List list = new ArrayList 集合的代理類
a , 通過獲取構造方法來建立
// 1,獲取動態代理類的Class
Class clazz = Proxy.getProxyClass(
List.class.getClassLoader(), // 目標類所實現的接口的類加載器
new Class[]{List.class} //目標類所實現的接口
);
// 2, 實現InvocationHandler接口,通過這個去調用目標類。
InvocationHandler ih = new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//可以加入功能
Object re = method.invoke(list, args); //調用所要代理的對象的方法
//可以加入功能
return re;
}
};
// 3,獲取代理類的構造函數
Constructor constructor = clazz.getConstructor(InvocationHandler.class);
// 4,創建代理對象
List myproxy = (List)constructor.newInstance(ih);
b , 直接通過newProxyInstance 來建立
List myproxy2 = (List)Proxy.newProxyInstance(
List.class.getClassLoader(), // 目標類所實現的接口的類加載器
new Class[]{List.class}, //目標類所實現的接口
new InvocationHandler(){ // 實現接口,去調用目標類
public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
//可以加入功能
Object re = method.invoke(list, args); //調用所要代理的對象的方法
//可以加入功能
return re;
}
}
);
示例代碼:
ackage proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collection;
interface SellInterface
{
public void sell();
}
class SellFactory implements SellInterface
{
public void sell() {
System.out.println("真實角色在處理問題");
}
}
//代理類1,儘量面向對向,使代理類更通用。
class MyProxy implements InvocationHandler
{
static Object obj;
MyProxy(Object obj) // obj:傳入須要代理的類
{
this.obj = obj;
}
//通過Proxy類的newProxyInstance方法來返回代理對象
public static Object factory(Object o)
{
Class clazz = o.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new MyProxy(o));
}
/*
* 實現InvocationHander接口的invoke
* proxy:代理實例
* method:對應於在代理實例上調用的接口方法的 Method 實例
* args [] :傳入代理實例上方法的參數值的對象數組.
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("函數調用前被攔截了"+method);
// System.out.println(proxy.getClass().getName());
//代理調用真實角色處理方法
Object mo = method.invoke(obj, args);
System.out.println("函數調用後進行處理"+method);
return mo;
}
}
//客戶端
public class DynamicProxy {
public static void main(String [] args) throws Exception
{
SellInterface mp = (SellInterface)MyProxy.factory(new SellFactory());
mp.sell();
// test();
}
public static void test() throws Exception
{
//獲取代理類Proxy的字節碼
System.out.println("-------------constructors list-------------");
Class clazzProxy = Proxy.getProxyClass(Object.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy.getName());
//獲取代理類Proxy的構造方法
Constructor [] constructors = clazzProxy.getConstructors();
//獲取Proxy的方法
System.out.println("-------------method list----------------");
Method [] methods = clazzProxy.getDeclaredMethods();
for(Method m : methods)
{
//以()形式輸出方法裏的參數
StringBuilder sb = new StringBuilder();
sb.append(m.getName());
sb.append("(");
//列出方法的參數
Class [] clazzParam = m.getParameterTypes();
for(Class c : clazzParam)
{
sb.append(c.getName()+",");
}
if(clazzParam!=null && clazzParam.length!=0)
sb.deleteCharAt(sb.length()-1);
sb.append(")");
System.out.println(sb);
}
//創建代理對象的方法
System.out.println("--------------create instance -------------------");
//創建構造器
Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class);
//方法一:
class MyInvocationHandler implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法一");
// Object retVal = method.invoke(obj, args);
return null;
}
}
Object proxy1 = constructor.newInstance(new MyInvocationHandler());
System.out.println(proxy1);
//方法二
Object proxy2 = constructor.newInstance(new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法二");
// Object retVal = method.invoke(obj, args);
return null;
}
});
System.out.println(proxy2);
//方法三:
Object proxy3 = Proxy.newProxyInstance(
Object.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("開始切面。。。"+method);
// Object retVal = method.invoke(obj, args); //動態調用真實角色處理方法
System.out.println("結束切面。。。"+method);
return null;
}
}
);
System.out.println(proxy3);
}
}
用代理實現AOP攔截機制的例子
可以攔截我們指定的函數,並在攔截前後根據需要進行處理.
除了攔截,代理模式還常用於資源加載,當我們要加載的資源很大時,我們可以讓真實主題角色在後臺加載資源,讓代理主題角色負責處理前臺的等待提示信息.
看下面代碼示例:
package proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collection;
interface AopInterface
{
public void start(Object obj); //切面--調用前的處理
public void end(Object obj); //切面--調用後處理
}
class AopInterfaceImp implements AopInterface //對象切面的具體操作
{
public void start(Object obj) {
System.out.println("調用前被攔截了,做一些其它的事");
}
public void end(Object obj) {
System.out.println("調用後被攔截,去處理一些其它事");
}
}
//代理類
class MyProxy4 implements InvocationHandler
{
private AopInterface aop; //切入時調用
private Object obj;
private String methodName = null; //要攔截的方法名字
MyProxy4() {} // obj:傳入須要代理的類
//通過Proxy類的newProxyInstance方法來返回代理對象
public Object factory(Object o)
{
this.obj = o;
Class clazz = o.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在這裏還應該做一些判斷,如:aop沒有初始化。
Object mo;
//如果該方法是要攔截的
if(methodName != null && method.toString().indexOf(methodName)!=-1) //這裏爲什麼是用indexOf呢?
//因爲method.toString() 返回的是
// public abstract void proxy.TestInterface.add() 形式。
{
aop.start(obj);
//代理調用真實角色處理方法
mo = method.invoke(obj, args);
aop.end(obj);
}
else
{
mo = method.invoke(obj, args);
}
return mo;
}
public AopInterface getAop()
{ return aop; }
public void setAop(AopInterface aop)
{ this.aop = aop; }
public String setMethodName(String methodName)
{
return this.methodName = methodName;
}
}
//用於測試的
interface TestInterface {
public void add();
public void dele();
}
class ImpTest implements TestInterface{
public void add() {
System.out.println("........... add()");
}
public void dele(){
System.out.println("............ acc()");
}
}
//客戶端
public class DynamicProxy2 {
public static void main(String [] args) throws Exception
{
MyProxy4 mp = new MyProxy4(); //建立代理對象
mp.setAop(new AopInterfaceImp()); //給代理設置所要代理的類
mp.setMethodName("add"); //設置須要被攔截的方法名
//通過代理建立測試類對象 ,因爲要讓測試類在執行時,先去代理處
TestInterface ai = (TestInterface)mp.factory(new ImpTest());
ai.add(); //執行要攔截的add()方法
ai.dele(); //執行沒有被攔截的dele()方法
}
}