這裡只是簡單的介紹下最基本的代理的使用。
代理,通俗點說 :就是一個人或者一個機構代表另一個人或者另一個機構採取行動。
在一些情況下,一個客戶不想或者不能夠直接引用一個對象,而代理對象可以在客戶
端和目標對象之前起到中介的作用。
代理模式給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。
UML圖
從上面的圖我們能看到代理涉及的角色:
抽象對象角色:聲明了目標對象和代理對象的共同介面,
這樣一來在任何可以使用目標對象的地方都可以使用代理對象。
目標對象角色:定義了代理對象所代表的目標對象。(也就是要代理的是誰)
代理對象角色:代理對象內部含有目標對象的引用,從而可以在任何時候
操作目標對象;代理對象提供一個與目標對象相同的介面,以便可以在任何時候替代目標對象。
代理對象通常在用戶端調用傳遞給目標對象之前或之後,執行某個操作,而不是單純地將調用
傳遞給目標對象。
我們在這裡示範一個房東,中介和客戶之間的關係(中介就是代理)
1,我們需要一個抽象的對象角色:(介面)
public interface IRenter {public abstract void rent();}
2,我們需要的目標對象:(就是實現的介面的類)
public class Renter implements IRenter{@Overridepublic void rent() {System.out.println("我是房東,開始收費咯咯");}}
3,代理對象的角色(就是我們的代理)
我們需要用到這個Java中代理使用的類
Object o =Proxy.newProxyInstance(loader, interfaces, h)
public class Client {@Testpublic 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>{@Overridepublic 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 Connectioncon=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() {@Overridepublic 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 blocke.printStackTrace();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.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() {@Overridepublic 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;}@Overridepublic 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),這不難理解。複雜隱藏代理和面板模式是不一樣的,因為代理控制訪問,而面板模式是不一樣的,因為代理控制訪問,而面板模式只提供另一組介面。