代理模式(Proxy Pattern),23種java經常使用設計模式之一。代理模式的定義:代理類對被代理對象提供一種代理以控制對這個對象的訪問。代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及過後處理消息等。我的理解:在生活中咱們經常把沒必要要的事情丟給別人去完成,而這些沒必要要的部分至關於他們代替咱們完成的,這就至關因而代理模式。java
例如:設計模式
一、明星與經紀人:就假設在和甲方談商演的時候,影視明星只負責決定是否去演出,而經紀人就須要先去聯繫甲方並和甲方商定角色和報酬,而後將結果告訴影視明星,影視明星決定是否去演出,經紀人在獲得明星的答覆後將結果通知甲方,走後面的流程。框架
二、中介與房東:在房屋租(售)的時候,租(購)房人會先找中介諮詢房屋信息,若是中介有鑰匙還能夠帶租(購)房人去看房,若是租(購)房人對房屋無購買意願,則本次流程結束;若是租(購)房人對房屋有興趣,則先與中介協商價格;若是中介以爲房東不會接受這個價格,則直接拒絕;若是中介以爲價格合理,則與房東溝通,中介將房東的決定告知租(購)房人,而後走接下來的流程。maven
一、當須要控制對一個對象的訪問,爲不一樣用戶提供不一樣級別的訪問權限時可使用保護代理模式。ide
二、當客戶端對象須要訪問遠程主機中的對象時可使用遠程代理模式,如RPC。性能
三、當須要爲一個對象的訪問(引用)提供一些額外的操做時可使用代理模式。測試
四、當須要用一個消耗資源較少的對象來表明一個消耗資源較多的對象,從而下降系統開銷、縮短運行時間時可使用虛擬代理,例如一個對象須要很長時間才能完成加載時。(待論證)this
五、當須要爲某一個被頻繁訪問的操做結果提供一個臨時存儲空間,以供多個客戶端共享訪問這些結果時可使用緩衝代理。經過使用緩衝代理,系統無須在客戶端每一次訪問時都從新執行操做,只需直接從臨時緩衝區獲取操做結果便可。(待論證)設計
靜態代理要求代理類必須繼承被代理對象的接口。至關於中介(代理類)知道房東(被代理對象)有哪幾個方面的要求(方法)。代理
代碼實現
房東接口
public interface ILandlord { //出售 String sell(int price); //出租 String lease(int leaseTime,int price); }
房東實現類
public class Landlord implements ILandlord { String houseName;//房屋名稱 int houseSellPrice;//房屋出售價格 int houseLeasePrice;//房屋出租價格(單價) int houseLeaseTime;//最短出租時間 @Override public String sell(int price) { if(price>=houseSellPrice){ return "我贊成以"+price+"元的價格出售"+houseName; }else{ return "我不一樣意以"+price+"元的價格出售"+houseName; } } @Override public String lease(int leaseTime, int price) { if(price<houseLeasePrice){ return "我不一樣意以"+price+"元的價格將"+houseName+"出租"; } if(leaseTime<houseLeaseTime){ return "我不一樣意將"+houseName+"出租"+leaseTime+"個月"; } return "我贊成以"+price+"元的價格將"+houseName+"出租"+leaseTime+"個月"; } public Landlord() { } public Landlord(String houseName, int houseSellPrice, int houseLeasePrice, int houseLeaseTime) { this.houseName = houseName; this.houseSellPrice = houseSellPrice; this.houseLeasePrice = houseLeasePrice; this.houseLeaseTime = houseLeaseTime; } public String getHouseName() { return houseName; } public void setHouseName(String houseName) { this.houseName = houseName; } public int getHouseSellPrice() { return houseSellPrice; } public void setHouseSellPrice(int houseSellPrice) { this.houseSellPrice = houseSellPrice; } public int getHouseLeasePrice() { return houseLeasePrice; } public void setHouseLeasePrice(int houseLeasePrice) { this.houseLeasePrice = houseLeasePrice; } public int getHouseLeaseTime() { return houseLeaseTime; } public void setHouseLeaseTime(int houseLeaseTime) { this.houseLeaseTime = houseLeaseTime; } }
中介類
public class Intermediary implements ILandlord { private ILandlord obj; Intermediary(ILandlord obj){ super(); this.obj=obj; } @Override public String sell(int price) { System.out.println("中介將購房人的出價告知房東"); String res = obj.sell(price); System.out.println("房東:"+res); System.out.println("中介將房東的結果告知購房人"); return "房東:"+res; } @Override public String lease(int leaseTime, int price) { System.out.println("中介將租房人的出價告知房東"); String res = obj.lease(leaseTime,price); System.out.println("房東:"+res); System.out.println("中介將房東的結果告知租房人"); return "房東:"+res; } }
測試類
public class StaticProxyTest { public static void main(String[] args) { Landlord landlordA=new Landlord("保利001號",3000000,2000,6); Intermediary proxy=new Intermediary(landlordA); String res = proxy.sell(3010000); System.out.println("購房者獲得的結果:"+res); res = proxy.lease(5,1998); System.out.println("購房者獲得的結果:"+res); } }
結果
中介將購房人的出價告知房東 房東:我贊成以3010000元的價格出售保利001號 中介將房東的結果告知購房人 購房者獲得的結果:房東:我贊成以3010000元的價格出售保利001號 中介將租房人的出價告知房東 房東:我不一樣意以1998元的價格將保利001號出租 中介將房東的結果告知租房人 購房者獲得的結果:房東:我不一樣意以1998元的價格將保利001號出租
JDK中的動態代理是在程序運行期間由JVM根據反射等機制動態的生成,因此不存在代理類的字節碼文件。代理類和委託類的關係是在程序運行時肯定。JDK中的動態代理使用的是java.lang.reflect.Proxy中的newProxyInstance方法來實現。
newProxyInstance方法的三個參數是ClassLoader loader、Class<?>[] interfaces和InvocationHandler h。ClassLoader loader:類加載器,負責將類的字節碼裝載到 Java 虛擬機(JVM)中併爲其定義類對象,而後該類才能被使用。Proxy 靜態方法生成動態代理類一樣須要經過類裝載器來進行裝載才能使用。
Class<?>[] interfaces:指明被代理類實現的接口,以後咱們經過拼接字節碼生成的類才能知道調用哪些方法。
InvocationHandler h:這是一個方法委託接口,每一個代理的實例都有一個與之關聯的 InvocationHandler 實現類,若是代理的方法被調用,那麼代理便會通知和轉發給內部的 InvocationHandler 實現類,由它的invoke方法決定處理。InvocationHandler 接口內部只是一個 invoke() 方法。
invoke方法的三個參數是Object proxy、 Method method和 Object[] args。
Object proxy:代理類實例。注意:這個代理類實例不是代理接口的實現類對象,而是JDK根據傳入的接口生成一個extends Proxy implements Interface的代理類的對象。這個對象其實就是Proxy.newProxyInstance方法生成的對象(結尾處作驗證)。
Method method:被調用的方法對象。
Object[] args:傳入的方法參數。
待代理的接口
public interface Star { /** * 唱歌 * @param song * @return */ String sing(String song); /** * 演戲 * @return */ String acting(); }
接口的實現類
/** * 歌星 */ public class SingingStar implements Star { @Override public String sing(String song) { System.out.println("明星本人表示願意唱:《"+song+"》"); return "願意唱:《"+song+"》"; } @Override public String acting() { System.out.println("明星本人表示不肯意演戲"); return "不肯意演戲"; } }
InvocationHandler接口實現類
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DynamicsStarProxy implements InvocationHandler { Object obj; DynamicsStarProxy(Object obj){ this.obj=obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("動態明星代理和甲方協商出演的酬勞"); Object res = method.invoke(obj, args); System.out.println("動態明星代理獲得明星的決定,並告知甲方"); return res; } }
測試類
public class DynamicsProxyTest { public static void main(String[] args) { Star taylorSwift=new SingingStar(); DynamicsStarProxy starProxy=new DynamicsStarProxy(taylorSwift); Star proxy= (Star) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),taylorSwift.getClass().getInterfaces(),starProxy); //甲方找黴黴演唱Mean String singRes=proxy.sing("Mean"); System.out.println("甲方獲得的結果:"+singRes); //甲方又想找黴黴演戲 String actRes=proxy.acting(); System.out.println("甲方獲得的結果:"+actRes); } }
結果
動態明星代理和甲方協商出演的酬勞 明星本人表示願意唱:《Mean》 動態明星代理獲得明星的決定,並告知甲方 甲方獲得的結果:願意唱:《Mean》 動態明星代理和甲方協商出演的酬勞 明星本表示不肯意演戲 動態明星代理獲得明星的決定,並告知甲方 甲方獲得的結果:不肯意演戲
Cglib是一個強大的,高性能,高質量的代碼生成類庫。它能夠在運行期擴展JAVA類與實現JAVA接口。其底層實現是經過ASM字節碼處理框架來轉換字節碼並生成新的類。CGLIB包的底層是經過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類,大部分功能其實是ASM所提供的,Cglib只是封裝了ASM,簡化了ASM操做。GLib動態代理不要求被代理類實現接口,任何一個類均可以被輕鬆的代理(final類不能,final方法也不能)。
Cglib動態代理須要自定義實現MethodInterceptor接口,在代理對象調用方法時,會被攔截到intercept方法中。public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
Object obj:cglib生成的代理對象,對應JDK動態代理中invoke方法的Object proxy。
Method method:被調用的代理對象方法,對應JDK動態代理中invoke方法的Method method。
Object[] args:方法入參,對應JDK動態代理中invoke方法的Object[] args。
MethodProxy proxy:被調用的方法的代理方法,CGLib動態代理經過該參數的invokeSuper方法來調用被代理對象的方法。
代碼實現
maven依賴
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
待代理的類
/** * 舞蹈名星 */ public class DanceStar { private String name;//藝名 //跳舞方法 public String dancing(String typeName){ System.out.println(name+"表示願意跳"+typeName); return name+"願意跳"+typeName; } public String getName() { return name; } public void setName(String name) { this.name = name; } DanceStar(){ super(); } DanceStar(String name){ super(); this.name=name; } }
Cglib代理類
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibStarProxy implements MethodInterceptor { /** * 自定義方法:利用Enhancer類生成代理類 * @param clazz * @param <T> * @return */ public <T> T getObjByEnhancer(Class<T> clazz){ Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); T res = (T) enhancer.create(); return res; } /** * 自定義方法:利用Enhancer類生成代理類(模擬JDK代理的用法) * @param target * @return */ private Object object; public Object getObjByEnhancer(Object target){ this.object=target; Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); Object res = enhancer.create(); return res; } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Cglib動態明星代理和甲方協商出演的酬勞"); Object res=null; res = proxy.invokeSuper(obj,args); //res = method.invoke(object, args);//模擬JDK代理的用法 System.out.println("Cglib動態明星代理獲得明星的決定,並告知甲方"); return res; } }
測試類
public class CglibStarProxyTest { public static void main(String[] args) { CglibStarProxy starProxy=new CglibStarProxy(); //DanceStar ssyy=new DanceStar("三上悠亞");//模擬JDK動態代理 DanceStar ssyy2=null; ssyy2 = starProxy.getObjByEnhancer(DanceStar.class); //ssyy2= (DanceStar) starProxy.getObjByEnhancer(ssyy);//模擬JDK動態代理 String res=ssyy2.dancing("鋼管舞"); System.out.println("甲方獲得的結果:"+res); } }
靜態代理:是經過靜態代理類實現了被代理類的接口的方式來實現的。
JDK動態代理:是經過JVM根據反射等機制動態的生成一個被代理類的接口的實現類實現的,該實現類的繼承方法會調用代理類中的invoke方法。
private MyInvocationHandler h; m3 = Class.forName("com.***.Star").getMethod("sing", new Class[] { Class.forName("java.lang.String") }); public final void sing(Sting song) { try { this.h.invoke(this, m3, new Object[] {song}); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }
CGLib動態代理:是使用ASM來生成被代理類的一個子類的字節碼,該子類會重寫父類的方法,在方法中調用代理類對象中的intercept()方法。
public final String dancing(String paramString) { MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0; if (tmp4_1 == null) { tmp4_1; CGLIB$BIND_CALLBACKS(this); } MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0; if (tmp17_14 != null) { return (String)tmp17_14.intercept(this, CGLIB$dancing$2$Method, new Object[] { paramString }, CGLIB$dancing$2$Proxy); } return super.dancing(paramString); }
接口
public interface Star { /** * 唱歌 * @param song 此處用對象封裝,能夠將代理生成的對象傳入 * @return */ Object sing(Object song); }
實現類
/** * 歌星 */ public class SingingStar implements Star { @Override public Object sing(Object song) { return song; } }
代理類
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DynamicsStarProxy implements InvocationHandler { Object obj; DynamicsStarProxy(Object obj){ this.obj=obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("sing".equals(method.getName())){//判斷是否爲sing方法 for(Object o:args){//sing方法c傳入的就是代理生成的對象 System.out.println(proxy==o);//判斷地址是否相同 } } Object res = method.invoke(obj, args); return res; } }
測試類
import java.lang.reflect.Proxy; public class DynamicsProxyTest { public static void main(String[] args) { Star taylorSwift=new SingingStar(); DynamicsStarProxy starProxy=new DynamicsStarProxy(taylorSwift); Star proxy= (Star) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),taylorSwift.getClass().getInterfaces(),starProxy); Object singRes=proxy.sing(proxy); } }
結果
true
感謝你看到這裏,文章有什麼不足還請指正,以爲文章對你有幫助的話記得給我點個贊,天天都會分享java相關技術文章或行業資訊,歡迎你們關注和轉發文章!