代理模式
1.生活中:
代理就是一個人或者一個組織代表其他人去做一件事。在一些情況下,一個對象不行或者不能夠直接引用一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。2.官方:
代理模式是對象的結構模式。代理模式給某一個對象提供一個代理對象,並有代理對象控制對原對象的引用。一,靜態代理
在代理模式中的角色●抽象主題角色:聲明瞭目標對象和代理對象的共同接口,這樣一來來任何可以使用目標對象的地方都可以使用代理對象
●真實主題角色:定義了代理對象所代表的目標對象
●代理主題角色:代理對象內部含有目標對象的引用,從而可以在任何時候操作目標對象:代理對象提供一個與目標對象相同的接口,以便可以在任何時候替代目標對象
代理對象通常在客戶端調用傳遞給目標對象之前或之後,執行某個操作,而不是淡出的將調用傳遞給目標對象它可以增加一些真實主題裏沒有的功能。
生活中的例子:過年加班比較忙,沒空去買火車票,這時可以打個電話到附近的票務中心,叫他們幫你買張回家的火車票,當然這會附加額外的勞務費。但要清楚票務中心自己並不賣票,只有火車站才真正賣票,票務中心賣給你的票其實是 通過火車站實現的。這點很重要!
上面這個例子,你就是“客戶”,票務中心就是“代理角色”,火車站是“真實角色”,賣票稱爲“抽象角色”!
代碼
//抽象主題角色
//抽象角色聲明真實對象和代理對象的共同接口
//抽象角色聲明真實對象和代理對象的共同接口 public interface TicketManager { / ** *售票 * / void soldTicket(); / ** *改簽 * / void changeTicket(); / ** *退票 * / void returnTicket(); }}
//真實主題角色
public class TicketManagerImpl implements TicketManager { @Override public void soldTicket() { System.out.println("售票"); } @Override public void changeTicket() { System.out.println("改簽"); } @Override public void returnTicket() { System.out.println("退票"); } /** *身份驗證 * / public void checkIdentity (){ System 。出去。println (“身份驗證” ); } }
//代理主題角色(添加了身份驗證功能)
public class StaticProxyTicketManager implements TicketManager{ private TicketManager ticketManager;//目標對象的引用 public StaticProxyTicketManager(TicketManager ticketManager) { this.ticketManager = ticketManager; } @Override public void soldTicket() { checkIdentity(); ticketManager.soldTicket(); } @Override public void changeTicket() { ticketManager.changeTicket (); } @覆蓋 公共無效returnTicket (){ ticketManager 。returnTicket (); } / ** *身份驗證 * / public void checkIdentity (){ System 。出去。println (“身份驗證--------------” ); } }
//第二個代理主題角色(添加了日誌功能)
//代理類 實現同一個接口 public class LogProxy implements TicketManager{ private TicketManager ticketManager; public LogProxy(TicketManager ticketManager) { this.ticketManager = ticketManager; } @Override public void soldTicket() { ticketManager.soldTicket(); log(); } @Override public void changeTicket() { ticketManager.changeTicket(); log (); } @覆蓋 公共無效returnTicket (){ ticketManager 。returnTicket (); log (); } //增強 private void log (){ System 。出去。println (“日誌...” ); } }
//客戶端
public class Test { public static void main(String[] args) { TicketManager tm = new LogProxy(new StaticProxyTicketManager(new TicketManagerImpl())); tm.soldTicket(); tm.changeTicket(); tm.returnTicket(); } }
結果:
身份驗證-----
售票
日誌...
身份驗證-----
改簽
日誌...
身份驗證-----
改簽
日誌...
從上面例子可以看出客戶端通過代理來購票而代理實際上不能賣票給客戶,他實際上是通過目標對象賣票給客戶的,也就是說他是通過真實主題的目標對象實現給客戶端賣票的功能,他只是一箇中介,但我們可以在它。裏面增加一些功能,身份比如驗證或者宣傳打廣告等其他的功能
靜態代理類:在程序運行前,代理類的的.class文件就已經存在了,確定已被代理的對象
靜態代理:
優點:
1。對真實對象進行封裝,不會修改目標類的代碼確定
:
1,多個不同類型的目標對象需要代理時,就需要建立多個代理類,造成類
的膨脹2,代碼的冗餘
3,編譯期加入,不夠靈活
二,動態代理
動態代理(Dynamic Proxy):相比靜態代理,動態代理具有更強的靈活性,他不用在設計實現時就指定某個代理類來代理哪一個被代理對象,
可把這種指定延遲到程序運行時由JVM實現。
所謂代理,就是需要代理類和被代理類有相同的對外接口或者說服務,
所以代理類一般都必須實現了所有被代理類已實現的接口,因爲接口就是指定了一系列對外服務的標準。
1.JDK實現動態代理
正因爲動態代理有這樣靈活的特性,所以我們在設計動態代理類(DynamicProxy)時,不用顯示地讓它實現與真實主題類(RealSubject)相同的接口(接口),從而把這種實現推遲到運行時。爲了能讓DynamicProxy類能夠在運行時纔去實現RealSubject類已實現的一系列接口並執行接口中相關的方法操作,需要讓DynamicProxy類實現JDK自帶的java.lang.reflect.InvocationHandler接口,接口中的invoke()方法能夠讓DynamicProxy實例在運行時調用被代理類的“對外服務”,即調用被代理類需要對外實現的所有接口中的方法,也就是完成對真實方法的調用,Java幫助文檔中稱這些真實方法爲處理程序。
按照上面所述,我們肯定必須先把被代理類RealSubject已實現的所有接口都加載到JVM中,不然JVM怎麼能夠找到這些方法呢?明白了這個道理,那麼我們就可以創建一個被代理類的實例,獲 該實例的類加載器類加載器。
所謂的類加載器類加載器,就是具有某個類的類定義,即類的內部相關結構(包括繼承樹,方法區等等)。
更重要的是,動態代理模式可以使得我們在不改變原來已有的代碼結構的情況下,對原來的“真實方法”進行擴展,增強其功能,並且可以達到控制被代理對象的行爲的目的。請詳見下面代碼中的DynamicProxy類,其中必須實現的調用()方法在調用被代理類的真實方法的前後都可進行一定的特殊操作這是動態代理最明顯的優點。
代碼:
public class DynamicProxyTicketManager implements InvocationHandler { private Object targetObject; /** * 反射,這樣你可以在不知道具體的類的情況下,根據配置的參數去調用一個類的方法。 * 在靈活編程的時候非常有用。 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 檢查 checkIdentity(); Object ret = null; try { // 調用目標方法 ret = method.invoke(targetObject, args); // 執行成功,打印成功信息 log(); } catch (Exception e) { e.printStackTrace(); // 失敗時,打印失敗信息 System.out.println("error-->>" + method.getName()); throw e; } return ret; } /** * 目標的初始化方法,根據目標生成代理類 * @param targetObject */ public Object newProxyInstance(Object targetObject){ this.targetObject=targetObject; /** * 第一個參數:目標對象的類加載器 * 第二個參數:目標接口已實現的所有接口,而這些是動態代理類要實現的接口列表 * 第三個參數:調用實現了InvocationHandler的對象生成動態代理實例,當你一調用代理, * 代理就會調用InvocationHandler的invoke方法 */ return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject。getClass ()。getInterfaces (), this ); } / ** *身份驗證 * / public void checkIdentity (){ System 。出去。println (“身份驗證--------------” ); } public void log (){ System 。出去。println (“日誌...” ); } }//客戶端
public class Test { public static void main(String[] args) { DynamicProxyTicketManager dynamicProxyTicketManager=new DynamicProxyTicketManager(); TicketManager tm=(TicketManager) dynamicProxyTicketManager.newProxyInstance(new TicketManagerImpl()); tm.soldTicket(); tm.changeTicket(); tm.returnTicket(); } }
優點:
1.一個動態代理類更加簡單了,可以解決創建多個靜態代理的麻煩,避免不斷的重複多餘的代碼2.調用目標代碼時,會在方法運行時動態的加入,決定你是什麼類型,才調動,靈活。
缺點:
1.系統靈活了,但是相比而言,效率降低了,比靜態代理慢一旦
2.動態代理比靜態代理在代碼的可讀性上差了一點,不太容易理解
3.JDK動態代理只能對實現了接口的類進行代理
總結
各有各的好,具體情況具體討論2.Cglib實現動態代理
AOP的源碼中使用了兩種動態代理來實現攔截切入功能,jdk動態代理和cglib動態代理。兩種方法同時存在,各有優劣。jdk
動態代理是由java內部的反射機制實現的.cglib動態代理代理底層是藉助asm實現的。
總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效。(可以通過將asm生成的類進行緩存,這樣解決ASM生成類過程低效的問題)。
還有一點要注意,JDK動態代理的前提,必須是目標類基於統一的接口。否則JDK動態代理不能應用。
由此可以看出JDK動態代理使用有一定的局部性.cglib這種第三方類庫實現的動態代理應用更加廣泛,且在效率上更有優勢。jdk
動態代理機制只能代理實現了接口的類,否則不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增 ,但因爲採用的是繼承,所以不能對最終修飾的類進行代理。