代理模式

原文地址:http://www.itdecent.cn/p/57dc69b9c418
qq群:614530228

代理模式

定義:給某個對象提供一個代理對象,并由代理對象控制對于原對象的訪問,即客戶不直接操控原對象,而是通過代理對象間接地操控原對象。

代理模式的理解

代理模式使用代理對象完成用戶請求,屏蔽用戶對真實對象的訪問?,F(xiàn)實世界的代理人被授權(quán)執(zhí)行當(dāng)事人的一些事宜,無需當(dāng)事人出面,從第三方的角度看,似乎當(dāng)事人并不存在,因為他只和代理人通信。而事實上代理人是要有當(dāng)事人的授權(quán),并且在核心問題上還需要請示當(dāng)事人。

在軟件設(shè)計中,使用代理模式的意圖也很多,比如因為安全原因需要屏蔽客戶端直接訪問真實對象,或者在遠(yuǎn)程調(diào)用中需要使用代理類處理遠(yuǎn)程方法調(diào)用的技術(shù)細(xì)節(jié),也可能為了提升系統(tǒng)性能,對真實對象進(jìn)行封裝,從而達(dá)到延遲加載的目的。

代理模式的參與者

代理模式的角色分四種:


角色.png

主題接口:Subject 是委托對象和代理對象都共同實現(xiàn)的接口,即代理類的所實現(xiàn)的行為接口。Request() 是委托對象和代理對象共同擁有的方法。
目標(biāo)對象:RealSubject 是原對象,也就是被代理的對象。
代理對象:Proxy 是代理對象,用來封裝真是主題類的代理類。
客戶端:使用代理類和主題接口完成一些工作。

代理模式的分類

代理的實現(xiàn)分為:
靜態(tài)代理:代理類是在編譯時就實現(xiàn)好的。也就是說 Java 編譯完成后代理類是一個實際的 class 文件。
動態(tài)代理:代理類是在運行時生成的。也就是說 Java 編譯完之后并沒有實際的class 文件,而是在運行時動態(tài)生成的類字節(jié)碼,并加載到JVM中。

動態(tài)代理的實現(xiàn)思路

  1. 代理對象和目標(biāo)對象均實現(xiàn)同一個行為接口。
  2. 代理類和目標(biāo)類分別具體實現(xiàn)接口邏輯。
  3. 在代理類的構(gòu)造函數(shù)中實例化一個目標(biāo)對象。
  4. 在代理類中調(diào)用目標(biāo)對象的行為接口。
  5. 客戶端想要調(diào)用目標(biāo)對象的行為接口,只能通過代理類來操作。

靜態(tài)代理模式的簡單實現(xiàn)

public class ProxyDemo {
    public static void main(String[] args) {
        SubjectProxy proxy = new SubjectProxy(new RealSubject());
        proxy.request();
    }
}

public interface Subject {
    void request();
}

public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("...request...");
    }
}

public class SubjectProxy implements Subject {

    Subject subject;

    public SubjectProxy(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void request() {
        subject.request();
    }
}

目標(biāo)對象(RealSubject )以及代理對象(Proxy)都實現(xiàn)了主題接口(Subject)。在代理對象(Proxy)中,通過構(gòu)造函數(shù)傳入目標(biāo)對象(RealSubject),然后重寫主題接口(Subject)的request()方法,在該方法中調(diào)用目標(biāo)對象(RealSubject )的request()方法,并可以添加一些額外的處理工作在目標(biāo)對象(RealSubject)的request()方法的前后。

代理模式的好處:
假如有這樣的需求,要在某些模塊方法調(diào)用前后加上一些統(tǒng)一的前后處理操作,比如在添加購物車、修改訂單等操作前后統(tǒng)一加上登陸驗證與日志記錄處理,該怎樣實現(xiàn)?首先想到最簡單的就是直接修改源碼,在對應(yīng)模塊的對應(yīng)方法前后添加操作。如果模塊很多,你會發(fā)現(xiàn),修改源碼不僅非常麻煩、難以維護(hù),而且會使代碼顯得十分臃腫。

這時候就輪到代理模式上場了,它可以在被調(diào)用方法前后加上自己的操作,而不需要更改被調(diào)用類的源碼,大大地降低了模塊之間的耦合性,體現(xiàn)了極大的優(yōu)勢。

靜態(tài)代理比較簡單,上面的簡單實例就是靜態(tài)代理的應(yīng)用方式,下面介紹本篇文章的主題:動態(tài)代理。

動態(tài)代理

動態(tài)代理的思路和上述思路一致,下面主要講解如何實現(xiàn)。

動態(tài)代理介紹
動態(tài)代理是指在運行時動態(tài)生成代理類。即,代理類的字節(jié)碼將在運行時生成并載入當(dāng)前代理的 ClassLoader。與靜態(tài)處理類相比,動態(tài)類有諸多好處。

  1. 不需要為(RealSubject )寫一個形式上完全一樣的封裝類,假如主題接口(Subject)中的方法很多,為每一個接口寫一個代理方法也很麻煩。如果接口有變動,則目標(biāo)對象和代理類都要修改,不利于系統(tǒng)維護(hù);
  2. 使用一些動態(tài)代理的生成方法甚至可以在運行時制定代理類的執(zhí)行邏輯,從而大大提升系統(tǒng)的靈活性。

動態(tài)代理涉及的主要類
主要涉及兩個類,這兩個類都是java.lang.reflect包下的類,內(nèi)部主要通過反射來實現(xiàn)的。
java.lang.reflect.Proxy:這是生成代理類的主類,通過 Proxy 類生成的代理類都繼承了 Proxy類。Proxy提供了用戶創(chuàng)建動態(tài)代理類和代理對象的靜態(tài)方法,它是所有動態(tài)代理類的父類。
java.lang.reflect.InvocationHandler:這里稱他為"調(diào)用處理器",它是一個接口。當(dāng)調(diào)用動態(tài)代理類中的方法時,將會直接轉(zhuǎn)接到執(zhí)行自定義的InvocationHandler中的invoke()方法。即我們動態(tài)生成的代理類需要完成的具體內(nèi)容需要自己定義一個類,而這個類必須實現(xiàn) InvocationHandler接口,通過重寫invoke()方法來執(zhí)行具體內(nèi)容。

Proxy提供了如下兩個方法來創(chuàng)建動態(tài)代理類和動態(tài)代理實例。

static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返
回代理類的java.lang.Class對象。第一個參數(shù)是類加載器對象(即哪個類加載
器來加載這個代理類到 JVM 的方法區(qū)),第二個參數(shù)是接口(表明你這個代
理類需要實現(xiàn)哪些接口),第三個參數(shù)是調(diào)用處理器類實例(指定代理類中具
體要干什么),該代理類將實現(xiàn)interfaces所指定的所有接口,執(zhí)行代理對象的
每個方法時都會被替換執(zhí)行InvocationHandler對象的invoke方法。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h) 返回代理類實例。參數(shù)與上述方法一致。

public interface UserService {

    String getUsername();

    int getAge();

}

public class UserServiceImpl implements UserService {
    @Override
    public String getUsername() {
        return "admin";
    }

    @Override
    public int getAge() {
        return 20;
    }
}

public class UserProxy implements InvocationHandler {

    private Object mProxy;

    public UserProxy(Object proxy) {
        this.mProxy = proxy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(mProxy, args);
    }
}

public class DynamicProxyDemo {

    public static void main(String[] args) {
        // 第一種方式
        UserService userService = new UserServiceImpl();
        UserProxy userProxy = new UserProxy(userService);
        UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), userProxy);
        System.out.println(proxy.getUsername() + "..." + proxy.getAge());

        // 第二種方式
         UserService proxy = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), new UserProxy(new UserServiceImpl()));
         System.out.println(proxy.getUsername() + "..." + proxy.getAge());
    }
}

The end--

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容