【設計模式】(十一)--結構型模式--代理模式

【設計模式】(十一)–結構型模式–代理模式

代理模式定義

Provide a surrogate or placeholder for another object to control access to it.

意思就是:爲其他對象提供一種代理一控制對這個對象的訪問。
代理模式(Proxy Pattern)也稱委託模式,在Java世界中經常遇見。後面接觸的狀態模式、策略模式、訪問者模式本質上也是代理模式,只是表現形式突出重點不大一樣而已。
代理模式一般有三個角色:

  1. 抽象 代理類與被代理類需要共同實現的接口。
  2. 代理類 也叫作委託類。持有被代理類的引用,在真是代理類方法執行的前後可以插入需要增強的內容。
  3. 被代理類 也叫被委託類,具體執行時會執行相關方法。

代理模式優點

  • 職責清晰。 被代理類只需要關注自身業務,對於多個同種類型的被代理類的共同業務能夠抽取至代理類中統一處理。
  • 高擴展性。 只要實現抽象,代理類持有新的被代理類,那麼就可以完成實際功能
  • 智能化。

代理模式的使用場景

按職責來劃分,通常有以下使用場景:

  • 1、遠程代理。
  • 2、虛擬代理。
  • 3、Copy-on-Write 代理。
  • 4、保護(Protect or Access)代理。
  • 5、Cache代理。
  • 6、防火牆(Firewall)代理。
  • 7、同步化(Synchronization)代理。
  • 8、智能引用(Smart Reference)代理。
    最常見的代理:遠程代理、虛擬代理、智能引用代理、保護代理

簡單的實現

類圖
在這裏插入圖片描述

實現

public class Test {
    public static void main(String[] args) {
        GasCooker gasCooker = new GasCooker();
        Cooker cooker = new CookerProxy(gasCooker);
        cooker.start();
        cooker.stop();
    }
}
public interface Cooker {
    void start();

    void stop();
}
public class GasCooker implements Cooker {
    @Override
    public void start() {
        System.out.println("煤氣竈點火成功!!大少開始煮飯炒菜吧!");
    }

    @Override
    public void stop() {
        System.out.println("炒菜結束,關火!");
    }
}
public class CookerProxy implements Cooker {

    private Cooker cooker;

    @Override
    public void start() {
        System.out.println("開始使用廚竈,開啓煤氣");
        cooker.start();
    }

    @Override
    public void stop() {
        cooker.stop();
        System.out.println("廚竈使用結束,關閉煤氣");
    }

    public CookerProxy(Cooker cooker) {
        this.cooker = cooker;
    }
}

結果
在這裏插入圖片描述

靜態代理

接上簡單的代理實現,也稱爲靜態代理,因爲程序運行前代理類的字節碼文件已經存在,代理類與被代理類的關係已經確定。

動態代理

動態代理類的源碼是在程序運行期間由JVM根據反射等機制動態的生成,所以不存在代理類的字節碼文件。代理類和委託類的關係是在程序運行時確定。

Java動態代理的常用實現

JDK原生動態代理

動態代理類和被代理類必須實現同一個接口。動態代理只能對接口中聲明的方法進行代理。
原生動態代理有兩個重要類

  • Proxy java.lang.reflect.Proxy是所有動態代理的父類。它通過靜態方法newProxyInstance()來創建動態代理的class對象和實例。
  • InvocationHandler 每一個動態代理實例都有一個關聯的InvocationHandler。通過代理實例調用方法,方法調用請求會被轉發給InvocationHandler的invoke方法

JDK原生動態代理實現

GasCooker依舊使用靜態代理的類

public class Test {
    public static void main(String[] args) {
        Cooker gasCooker = new GasCooker();
        CookerStartHandler cookerStartHandler = new CookerStartHandler();
        CookerStopHandler cookerStopHandler = new CookerStopHandler();
        Cooker cookerStartProxy = (Cooker) cookerStartHandler.getProxy(gasCooker);
        cookerStartProxy.start();
        Cooker cookerStopProxy = (Cooker) cookerStopHandler.getProxy(gasCooker);
        cookerStopProxy.stop();
    }
}

class CookerStartHandler implements InvocationHandler {
    Object obj;

    public Object getProxy(Object obj) {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doBefore();
        Object result = method.invoke(obj, args);
        doAfter();
        return result;
    }

    private void doBefore() {
        System.out.println("JDK動態代理--開始使用廚竈,開啓煤氣");
    }

    private void doAfter() {
        System.out.println("JDK動態代理--開啓了,就維持火力");
    }
}

class CookerStopHandler implements InvocationHandler {
    Object obj;

    public Object getProxy(Object obj) {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doBefore();
        Object result = method.invoke(obj, args);
        doAfter();
        return result;
    }

    private void doBefore() {
        System.out.println("JDK動態代理--要關火了,等着關煤氣");
    }

    private void doAfter() {
        System.out.println("JDK動態代理--廚竈使用結束,關閉煤氣");
    }
}

結果
在這裏插入圖片描述

CGLib動態代理

CGLib(Code Generation Library)是一個基於ASM的字節碼生成庫。它允許我們在運行時對字節碼進行修改或動態生成。CGLib通過繼承被代理類的方式實現代理。
CGLib動態代理有兩個重要類

  • Enhancer Enhancer指定要代理的目標對象。通過create方法得到代理對象。通過代理實例調用非final方法,方法調用請求會首先轉發給MethodInterceptor的intercept
  • MethodInterceptor 通過代理實例調用方法,調用請求都會轉發給intercept方法進行增強。

CGLib動態代理實現

maven 依賴

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

public class Test {
    public static void main(String[] args) {
        Cooker cooker = new GasCooker();
        CookerStartInterceptor startInterceptor = new CookerStartInterceptor();
        CookerStopInterceptor stopInterceptor = new CookerStopInterceptor();
        Cooker startProxy = (Cooker) startInterceptor.getProxy(cooker);
        Cooker stopProxy = (Cooker) stopInterceptor.getProxy(cooker);
        startProxy.start();
        stopProxy.stop();
    }
}

class CookerStartInterceptor implements MethodInterceptor {
    Object target;

    public Object getProxy(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回調方法
        enhancer.setCallback(this);
        // 創建代理對象
        return enhancer.create();
    }

    private void doBefore() {
        System.out.println("CGLib動態代理--開始使用廚竈,開啓煤氣");
    }

    private void doAfter() {
        System.out.println("CGLib動態代理--開啓了,就維持火力");
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        doBefore();
        Object result = methodProxy.invoke(target, objects);
        doAfter();
        return result;
    }
}

class CookerStopInterceptor implements MethodInterceptor {
    Object target;

    public Object getProxy(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回調方法
        enhancer.setCallback(this);
        // 創建代理對象
        return enhancer.create();
    }

    private void doBefore() {
        System.out.println("CGLib動態代理--要關火了,等着關煤氣");
    }

    private void doAfter() {
        System.out.println("CGLib動態代理--廚竈使用結束,關閉煤氣");
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        doBefore();
        Object result = methodProxy.invoke(target, objects);
        doAfter();
        return result;
    }
}

結果
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章