JAVA設計模式之----------代理模式(Proxy)
這裡只是簡單的介紹下最基本的代理的使用。
代理,通俗點說 :就是一個人或者一個機構代表另一個人或者另一個機構採取行動。
在一些情況下,一個客戶不想或者不能夠直接引用一個物件,而代理物件可以在客戶
端和目標物件之前起到中介的作用。代理模式給某一個物件提供一個代理物件,並由代理物件控制對原物件的引用。
UML圖
從上面的圖我們能看到代理涉及的角色:
抽象物件角色:聲明瞭目標物件和代理物件的共同介面,
這樣一來在任何可以使用目標物件的地方都可以使用代理物件。
目標物件角色:定義了代理物件所代表的目標物件。(也就是要代理的是誰)
代理物件角色:代理物件內部含有目標物件的引用,從而可以在任何時候
操作目標物件;代理物件提供一個與目標物件相同的介面,以便可以在任何時候替代目標物件。
代理物件通常在客戶端呼叫傳遞給目標物件之前或之後,執行某個操作,而不是單純地將呼叫
傳遞給目標物件。
我們在這裡演示一個房東,中介和客戶之間的關係(中介就是代理)
1,我們需要一個抽象的物件角色:(介面)
public interface IRenter {
public abstract void rent();
}
2,我們需要的目標物件:(就是實現的介面的類)
public class Renter implements IRenter{ @Override public void rent() { System.out.println("我是房東,開始收費咯咯"); } }
3,代理物件的角色(就是我們的代理)
我們需要用到這個Java中代理使用的類
Object o =Proxy.newProxyInstance(loader, interfaces, h)
public class Client { @Test public void Test2(){ final Renter r =new Renter();//被代理的物件 //o是中介代理之後的物件 Object o =Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{IRenter.class},new InvocationHandler() <span style="white-space:pre"> </span>{ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//從這裡開始就是開始攔截要控制的內容了,如if(method.getName.equals('rent')){將方法名為rent的函式,做出你想要的事情}
// System.out.println("aaaa");//這裡只是簡單的每個方法之前輸出
System.out.println("收點費用");
return method.invoke(r, args); //<span style="font-family: 'ms shell dlg';">return method.invoke(要代理的物件, 引數傳進來什麼引數就放出什麼引數,這裡相當於全部放開);
}
} );
IRenter ir =(IRenter) o;//最後在使用的時候需要 將物件 強轉為介面型別的
ir.rent();
}
}
通過上面的簡單介紹,能發現,一個代理類的所有方法或函式,都要經過代理之後,才會進行操作,指定操作的方法做我們想做的事情,沒有指定的直接放行。
上面的只是一個簡單的介紹例子,下面介紹一個在實際中的使用例子。
我們在連線資料庫的時候,都是採用的連結池的方式,但是連結池裡面的連線都是有限個的,
所以我們需要每個用完之後就將其放回池中,但是單獨去寫一個函式將其放回池中,不太好用,
所以,我們就將 con.close()關閉連線的時候,不去關,而是直接的放回池中,
讓其他的執行緒來呼叫,也就是需要攔截con裡面的close方法。
所需要的三個角色:
1,介面物件 connection介面
2,實現物件 Connectioncon=DriverManager.getConnection(url, user, password);//在進行連線的時候可以。
3,需要我們的代理類。
下面這是整個連線池的程式碼:
public class hibernateFactory2 {
private static final int NUM=3;
private static List<Connection> pool =new ArrayList<Connection>();
static{
//讀取配置檔案
Properties p =new Properties();
try {
p.load(hibernateFactory.class.getClassLoader().getResourceAsStream("jdbc.properties"));
//讀取裡面的值,一直修改配置檔案即可
String driver=p.getProperty("driver");
String url=p.getProperty("url");
String user=p.getProperty("username");
String password=p.getProperty("password");
System.out.println(driver+url+user+password);
Class.forName(driver);
for(int i=0;i<NUM;i++){
final Connection con=DriverManager.getConnection(url, user, password);
//採用動態代理開始進行對connection介面實現代理,對con.close,實現換回去
</span><span style="color:#ff0000;">Object o =Proxy.newProxyInstance(hibernateFactory2.class.getClassLoader(), new Class[]{Connection.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("close")){ //攔截close方法
pool.add((Connection)(proxy));//將連線還回池
System.out.println("換回來了。。。");
return null;
}
return method.invoke(con, args);//其餘的全部放行
}
});
pool.add((Connection)o);
}
// System.out.println("初始化完畢"+con);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//下面是獲取連線。
public static synchronized Connection getCon() throws Exception{
if(pool.size()<=0){
System.out.println("pool中已經沒有連線");
return getCon() ;
}
System.out.println("使用一個。。");
return pool.remove(0);
//兩種方法都是可以的
// while(pool.size()<=0){
// System.out.println("池中已經乜有連線");
// Thread.sleep(1000);
// }
// return pool.remove(0);
// }
}}
在上面的基礎上,我們寫了一個通用的版本
public class proxyUtils {
public static Object getProxy(final Object srcobj){
Object o = Proxy.newProxyInstance(proxyUtils.class.getClassLoader(),
srcobj.getClass().getInterfaces(), //這句可以修改
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("被攔擊了。。。。");//這裡採用攔截
return method.invoke(srcobj, args);//返回值我們也可以進行一些列的操作
}
}
);
return o;
}
}
但是我們在呼叫的這個工具的時候,必須傳進來一個例項物件,也就是實現了的類
如 IA 為介面 A 為實現類
A a =new A();
IA ia =(IA)proxyUtils.getProxy(a);
通過ia 的物件呼叫方法就可以。
上面方法的另一種實現形式
public class proxyUtils2 implements InvocationHandler{
private Object srcobj;
public proxyUtils2(Object srcobj) {
this.srcobj=srcobj;
}
public static Object getProxy(final Object srcobj){
Object o = Proxy.newProxyInstance(proxyUtils2.class.getClassLoader(),
srcobj.getClass().getInterfaces(), //這句可以修改 ,在不知道的情況下,這種方法最好
new proxyUtils2(srcobj));
return o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("在這裡採用攔截");
Object obj =method.invoke(srcobj, args);//在這裡我們還可以進行對返回的值進行操作,這也是代理帶來的好處
return obj;
}
}
上面的這些都是採用代理設計模式實現的,其實 代理模式就相當於一個祕書,能幫你做你想做的事情。
代理應用方面(從別人哪裡看到的)
代理模式的應用形式
(1)遠端代理(Remote Proxy) -可以隱藏一個物件存在於不同地址空間的事實。也使得客戶端可以訪問在遠端機器上的物件,遠端機器可能具有更好的計算效能與處理速度,可以快速響應並處理客戶端請求。
(2)虛擬代理(Virtual Proxy) – 允許記憶體開銷較大的物件在需要的時候建立。只有我們真正需要這個物件的時候才建立。
(3)寫入時複製代理(Copy-On-Write Proxy) – 用來控制物件的複製,方法是延遲物件的複製,直到客戶真的需要為止。是虛擬代理的一個變體。
(4)保護代理(Protection (Access)Proxy) – 為不同的客戶提供不同級別的目標物件訪問許可權
(5)快取代理(Cache Proxy) – 為開銷大的運算結果提供暫時儲存,它允許多個客戶共享結果,以減少計算或網路延遲。
(6)防火牆代理(Firewall Proxy) – 控制網路資源的訪問,保護主題免於惡意客戶的侵害。
(7)同步代理(SynchronizationProxy) – 在多執行緒的情況下為主題提供安全的訪問。
(8)智慧引用代理(Smart ReferenceProxy) - 當一個物件被引用時,提供一些額外的操作,比如將對此物件呼叫的次數記錄下來等。
(9)複雜隱藏代理(Complexity HidingProxy) – 用來隱藏一個類的複雜集合的複雜度,並進行訪問控制。有時候也稱為外觀代理(Façade Proxy),這不難理解。複雜隱藏代理和外觀模式是不一樣的,因為代理控制訪問,而外觀模式是不一樣的,因為代理控制訪問,而外觀模式只提供另一組介面。