国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

代理模式

2019-11-08 18:21:03
字體:
來源:轉載
供稿:網友

代理模式

代理模式的定義很簡單:給某一對象提供一個代理對象,并由代理對象控制對原對象的引用。

 

代理模式的結構

有些情況下,一個客戶不想活著不能夠直接引用一個對象,可以通過代理對象在客戶端和目標對象之間起到中介作用。代理模式中的角色有:

1、抽象對象角色

聲明了目標對象和代理對象的共同接口,這樣一來在任何可以使用目標對象的地方都可以使用代理對象

2、目標對象角色

定義了代理對象所代表的目標對象

3、代理對象角色

代理對象內部含有目標對象的引用,從而可以在任何時候操作目標對象;代理對象提供一個與目標對象相同的接口,以便可以在任何時候替代目標對象

 

靜態代理示例

假如一個接口里面有一個方法,想在調用這個接口的前后都加一點東西,就可以使用代理模式。靜態代理是代理模式最簡單的實現,先定義一個靜態代理接口,里面有一個PRint()方法,看一下:

public interface StaticHelloWorld{    // 定義一個接口,里面有一個打印方法    void print();}

讓一個子類實現它,打印一句"Hello World"出來:

復制代碼
public class StaticHelloWorldImpl implements StaticHelloWorld{    public void print()    {        System.out.println("Hello World");    }}復制代碼

給這個接口創建一個代理對象,來實現對接口實現類的代理。注意,這里的重點是代理對象和實際對象實現的是同一個接口,因為希望在任何時候讓代理對象替代實際對象:

復制代碼
public class StaticProxy implements StaticHelloWorld{    private StaticHelloWorld staticHelloWorld;        public StaticProxy(StaticHelloWorld staticHelloWorldImpl)    {        this.staticHelloWorld = staticHelloWorldImpl;    }        public void print()    {        System.out.println("Before Hello World!");        staticHelloWorld.print();        System.out.println("After Hello World!");    }}復制代碼

寫一個類去調用代理對象,在代理對象的構造函數中傳入一個實際對象即可:

復制代碼
public class StaticTestMain{    public static void main(String[] args)    {        StaticHelloWorld shw = new StaticHelloWorldImpl();        StaticProxy sp = new StaticProxy(shw);        sp.print();    }}復制代碼

運行結果為:

Before Hello World!Hello WorldAfter Hello World!

這個很明顯,就不說了。

 

靜態代理的缺點

靜態代理的特點是靜態代理的代理類是程序員創建的,在程序運行之前靜態代理的.class文件已經存在了。

從靜態代理來看,看到靜態代理模式確實可以有一個代理對象來控制實際對象的引用,并通過代理對象來使用實際對象。這種模式在代理量較小的時候還可以,但是代理量一大起來,就存在著三個比較大的缺點:

1、如果想換一種代理內容,比如我在"Hello World"前后不想輸入"Before XXX"和"After XXX"了,想輸出運行前后系統當前時間,就必須新寫一個代理對象。這樣很容易造成代理對象的膨脹。

2、代理內容無法復用,也就是說"Before XXX"和"After XXX"只可以給某一個類使用,另一個類如果也想使用這個代理內容,必須自己也寫一個,同樣,造成的后果就是代理類的無限膨脹

3、接口里面如果新增了一個方法,實際對象實現了這個方法,代理對象也必須新增內容,去給這個新增方法增加代理內容(假如需要的話)

 

利用JDK中的代理類Proxy實現動態代理的示例

由于靜態代理的局限性,所以產生了動態代理的概念。看一下,首先還是定義一個動態代理接口:

public interface DynamicHelloWorld{    // 動態代理類,有一個print()方法    String print();}

寫一個類去實現它,打印方法打印"Enter DynamicHelloWorldImpl.print()"并返回"DynamicHelloWorldImpl":

復制代碼
public class DynamicHelloWorldImpl implements DynamicHelloWorld{    public String print()    {        System.out.println("Enter DynamicHelloWorldImpl.print()");                return "DynamicHelloWorldImpl";    }}復制代碼

最關鍵的一部分,動態代理類,也是不太好理解的一部分。在java中,動態代理需要實現InvocationHandler接口。

InvocationHandler接口里面只有一個方法invoke(),至于如何實現,完全看使用者自己的喜好,沒有固定。可以像下面這樣,把newInstance即生成一個動態代理類的過程放到InvocationHandler的實現類中:

復制代碼
public class DynamicProxy implements InvocationHandler{    private Object target;        public Object newInstance(Object target)    {        this.target = target;                return Proxy.newProxyInstance(target.getClass().getClassLoader(),                 target.getClass().getInterfaces(), this);    }        public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable    {        System.out.println("Before DynamicProxy");        method.invoke(target, args);        System.out.println("After DynamicProxy");        return null;    }}復制代碼

也可以像下面這樣,讓動態代理類在外面生成,只在構造函數中傳入一個target:

復制代碼
public class DynamicProxy implements InvocationHandler{    private Object target;        public DynamicProxy(Object target)    {        this.target = target;    }        public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable    {        System.out.println("Before DynamicProxy");        method.invoke(target, args);        System.out.println("After DynamicProxy");        return null;    }}復制代碼

如果是前者的寫法,那么main函數要這么寫:

復制代碼
public class DynamicTestMain{    public static void main(String[] args) throws Exception    {        DynamicProxy dp = new DynamicProxy();        DynamicHelloWorld dhwi = new DynamicHelloWorldImpl();        DynamicHelloWorld dhw = (DynamicHelloWorld)dp.newInstance(dhwi);        dhw.print();    }}復制代碼

如果是后者的寫法,那么main函數要這么寫:

復制代碼
public class DynamicTestMain{    public static void main(String[] args) throws Exception    {        DynamicHelloWorld dhwi = new DynamicHelloWorldImpl();        InvocationHandler ih = new DynamicProxy(dhwi);        DynamicHelloWorld dhw =                 (DynamicHelloWorld)Proxy.                newProxyInstance(DynamicHelloWorld.class.getClassLoader(),                                  new Class<?>[]{DynamicHelloWorld.class}, ih);        dhw.print();    }}復制代碼

不管哪種寫法,運行結果都是一樣的:

Before DynamicProxyEnter DynamicHelloWorldImpl.print()After DynamicProxy

 

動態代理解析

上面兩種寫法,本質上都是一樣的。萬變不離其宗,歸納起來,實現一個動態代理可以總結為如下四步:

1、獲取要被代理的對象,也就是實際對象

2、實現InvocationHandler接口,生成實際的代理內容

3、利用Proxy.newInstance()方法生成一個代理內容,第三個參數傳入InvocationHandler的實現類

4、代理對象調用接口內部的方法

動態代理,利用動態編譯+反射技術,把對實際對象的方法調用轉換成對傳入的InvocationHandler接口實現類的invoke方法的調用,這是動態代理模式實現的關鍵點。要證明這一點并不難,在程序中打上斷點跟一下代碼即可:

按F5執行下一步,看到程序走到了InvocationHandler實現類的invoke方法中:

執行完methd.invoke(target, args),看到控制臺打印出了"Enter DynamicHelloWorldImpl.print()"這一句語句,說明執行了實際對象DynamicHelloWorldImpl的print()方法:

這證明了我們的結論,把對于實際對象方法的調用轉換為對于InvocationHandler實現類的invoke方法的調用

 

動態代理的優點

1、最直觀的,類少了很多

2、代理內容也就是InvocationHandler接口的實現類可以復用,可以給A接口用、也可以給B接口用,A接口用了InvocationHandler接口實現類A的代理,不想用了,可以方便地換成InvocationHandler接口實現B的代理

3、最重要的,用了動態代理,就可以在不修改原來代碼的基礎上,就在原來代碼的基礎上做操作,這就是AOP即面向切面編程

 

動態代理的缺點

動態代理有一個最大的缺點,就是它只能針對接口生成代理,不能只針對某一個類生成代理,比方說我們在調用Proxy的newProxyInstance方法的時候,第二個參數傳某個具體類的getClass(),那么會報錯:

Exception in thread "main" java.lang.IllegalArgumentException: proxy.DynamicHelloWorldImpl is not an interface

這是因為java.lang.reflect.Proxy的newProxyInstance方法會判斷傳入的Class是不是一個接口:

復制代碼
.../*  * Verify that the Class object actually represents an  * interface.  */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException(    interfaceClass.getName() + " is not an interface");}...復制代碼

而實際使用中,我們為某一個單獨的類實現一個代理也很正常,這種情況下,我們就可以考慮使用CGLIB(一種字節碼增強技術)來為某一個類實現代理了。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 武功县| 大洼县| 腾冲县| 讷河市| 松滋市| 马鞍山市| 上高县| 罗城| 龙门县| 海晏县| 旌德县| 岳阳市| 阿坝县| 隆安县| 壤塘县| 南溪县| 阿勒泰市| 娱乐| 喀喇沁旗| 葫芦岛市| 开原市| 府谷县| 安庆市| 乐至县| 邢台县| 乌拉特中旗| 瑞安市| 隆昌县| 房产| 外汇| 凌云县| 鹰潭市| 兴城市| 彭阳县| 古交市| 永修县| 上饶县| 宜宾市| 贞丰县| 监利县| 乌兰浩特市|