設計模式——代理模式

糖拌蕃茄發表於2021-01-04

模式介紹

代理模式:為其他物件提供一種代理以控制對這個物件的訪問。
代理模式(Proxy)是一種結構型設計模式,主要解決的問題是:在直接訪問物件時帶來的問題.

分類

  • 靜態代理:代理類是在編譯時就實現好的。也就是說 Java 編譯完成後代理類是一個實際的 class 檔案。

  • 動態代理:代理類是在執行時生成的。也就是說 Java 編譯完之後並沒有實際的 class 檔案,而是在執行時動態生成的類位元組碼,並載入到JVM中。

靜態代理模式結構

模式涉及角色

RealSubject 是原物件(或稱委託物件),ProxyObject是代理物件。
Subject 是委託物件和代理物件都共同實現的介面。
leave() 是委託物件和代理物件共同擁有的方法。

結構圖

這裡寫圖片描述
(圖片來自網路,侵刪)

示例

比如某人要請假,因為有事不能直接去請假,可以找個同事(代理物件)幫他請假。

程式碼實現

/**
 * ClassName: ProxyPattern <br/>
 * Function: 靜態代理和動態代理<br/>
 *
 * @author gary.liu
 * @date 2017/5/23
 */
public class ProxyPattern {

    public static void main(String[] args){

        /**
         * 靜態代理測試
         */
        RealSubject realSubject = new RealSubject();  //委託物件
        ProxyObject proxyObject = new ProxyObject(realSubject);  //代理物件

        proxyObject.leave();

    }
}

interface Subject{
    /**
     * 請假介面
     */
    void leave();
}

class RealSubject implements Subject {

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

}

class ProxyObject implements Subject {

    private Subject subject;

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

    @Override
    public void leave(){
        System.out.println("真正物件告訴代理幫他請假");
        subject.leave();
        System.out.println("代理告訴真正物件請假成功");
    }

}

動態代理

動態代理的思維模式與之前的一般模式是一樣的,也是面向介面進行編碼,建立代理類將具體類隱藏解耦,不同之處在於代理類的建立時機不同,動態代理需要在執行時因需實時建立.

模式結構

和上面類似,需要一個介面和實現了這個介面的真實物件類,然後還要自己定義一個類(呼叫處理器類,即實現 InvocationHandler 介面),這個類的目的是指定執行時將生成的代理類需要完成的具體任務(包括Preprocess和Postprocess),即代理類呼叫任何方法都會經過這個呼叫處理器類。

結構圖

這裡寫圖片描述
(圖片來自網路,侵刪)

程式碼實現

用動態代理實現上面的場景,需要自定義一個呼叫處理器類。

public class ProxyPattern {

    public static void main(String[] args){

        /**
         * 動態代理測試
         */
        ProxyHandler proxyHandler = new ProxyHandler(realSubject);
        //動態生成代理物件
        Subject proxySubject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(),
                proxyHandler);
        proxySubject.leave();

    }
}

/**
 * 動態代理實現上面的例子
 *
 */
class ProxyHandler implements InvocationHandler {

    private Subject subject;

    public ProxyHandler(Subject subject){

        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args){

        Object result = null;
        System.out.println("真正物件告訴代理幫他請假");
        try{
            result = method.invoke(subject, args);

        } catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("代理告訴真正物件請假成功");

        return result;
    }

}

jdk動態代理為什麼要求委託物件實現介面

//建立代理物件  
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

第二個引數是介面,表明這個代理類需要實現哪些介面。
因為 Java 的單繼承特性(每個代理類都繼承了 Proxy 類),只能針對介面建立代理類,不能針對類建立代理類。

手動生成的代理類寫入位元組陣列中

        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy1", RealSubject.class.getInterfaces());

然後寫入class檔案,用 jd-gui反編譯生產的代理類如下。

public final class $Proxy1 extends java.lang.reflect.Proxy implements com.lzhenxing.javascaffold.javabase.designpattern.Subject {

模式應用

代理模式用到的地方很多,比如 spring aop 使用的便是動態代理模式,spring aop 有兩種實現方式,一種是jdk動態代理(要求委託物件實現介面);另一種是位元組碼增強,委託物件可以不實現介面,具體實現如 cglib。

參考資料

代理模式及Java實現動態代理

《大話設計模式》

相關文章