首頁>技術>

代理模式(Proxy Pattern),透過一個代理類,來支援原本想呼叫類的功能,並能在原有類的基礎上,附加其他功能。也就是說我原本想呼叫A類,但是現在我改成呼叫B類,並且B類擁有完整的A的功能,對呼叫方依然可以拿到A的結果,而且還可以在呼叫前後附加其他功能,或者是修改結果。

但是為什麼這麼做呢?

透過代理類,可以在呼叫A類目標方法的前後,附加一些功能,比如在A類的business()方法前後記錄日誌、記錄時間進行效能監控等。

這樣就能讓我們即擁有A的完整功能,又能擴展出其他我們需要的功能。

總結來說:代理模式將和業務不相關的功能,剝離出來,交給其他角兒來完成,從而使我們的業務類更專注與業務程式碼。

代理類分為靜態代理和動態代理。

我們先來看看靜態代理。

我們在demo中新增兩個基於使用者的方法,一個是使用者登入,一個是模擬一個使用者業務計算的方法。其中使用者登入,我們讓代理類除了登入以外,還附加日誌記錄功能,使用者每次登入時都進行日誌記錄;另一個業務計算方法,我們讓代理類另外完成效能監控功能。

1、建立抽象使用者類

package com.my.study.design.proxy;/** * www.itzhimei.com */public interface IUser {    void login(String userName,String pwd);    void business() throws InterruptedException;}

2、實現使用者類

package com.my.study.design.proxy;/** * www.itzhimei.com */public class User implements IUser {    @Override    public void login(String userName,String pwd) {        System.out.println("使用者["+userName +"]登入.");    }    @Override    public void business() throws InterruptedException {        System.out.println("執行業務方法計算中....");        Thread.sleep(500L);        System.out.println("執行業務方法計算完成");    }}

3、定義代理類,代理類也實現抽象使用者方法,讓代理類看起來和使用者類是一樣的,具備相同能力。

package com.my.study.design.proxy;/** * www.itzhimei.com */public class Proxy implements IUser {    private IUser user;    public Proxy(IUser user) {        this.user = user;    }    @Override    public void login(String userName, String pwd) {        //登入時進行日誌列印        System.out.println("使用者["+userName +"]登入日誌記錄開始...");        user.login(userName, pwd);        System.out.println("使用者["+userName +"]登入日誌記錄結束...");    }    @Override    public void business() throws InterruptedException {        long startTimestamp = System.currentTimeMillis();        user.business();        long endTimeStamp = System.currentTimeMillis();        long t = endTimeStamp - startTimestamp;        System.out.println("使用者執行業務計算總用時:"+ t);    }}

在代理類中,持有了一個User物件,真正的使用者業務,代理類會呼叫使用者類來處理,代理類在呼叫使用者類之前或之後附加其他功能。

4、測試

package com.my.study.design.proxy;/** * www.itzhimei.com */public class Client {    public static void main(String[] args) throws InterruptedException {        IUser user = new User();        IUser proxy = new Proxy(user);        proxy.login("張三","123");        System.out.println("----------------------------------");        proxy.business();    }}

輸出:

使用者[張三]登入日誌記錄開始...使用者類輸出:使用者[張三]登入.代理類輸出:使用者[張三]登入日誌記錄結束...----------------------------------使用者類輸出:執行業務方法計算中....使用者類輸出:執行業務方法計算完成代理類輸出:使用者執行業務計算總用時:500

以上就是一個靜態代理的完整實現。

類關係圖:

靜態代理有一個問題就是,如果被代理的目標類非常多,那我們就要生成非常多的代理類,比如我們上一節的例子中,我要記錄效能的類,如果有100+個,那我就要再寫100多個代理類,這顯然是不合理的。

這是動態代理就登場了。

動態代理就是不用事先為每一個被代理的目標類,一一生成代理類,而是在執行時,按照需要動態生成代理類,來完成代理工作。

Java本身就實現了動態代理功能,其原理是基於反射實現的。

我們來看一下上一節的靜態代理,如何演變為動態代理。

1、定義抽象使用者類

package com.my.study.design.proxy.dynamic;/** * www.itzhimei.com */public interface IUser {    void business() throws InterruptedException;}

2、定義具體使用者類,實現抽象使用者類

package com.my.study.design.proxy.dynamic;/** * www.itzhimei.com */public class User implements IUser {    @Override    public void business() throws InterruptedException {        System.out.println("使用者類輸出:執行業務方法計算中....");        Thread.sleep(500L);        System.out.println("使用者類輸出:執行業務方法計算完成");    }}

3、定義動態代理的業務處理類,也就是代理的處理邏輯,要寫在這裡

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** * www.itzhimei.com */public class DynamicProxy implements InvocationHandler {    private Object realObject;    public DynamicProxy(Object realObject) {        this.realObject = realObject;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("動態代理輸出:前置業務方法計算中....");        method.invoke(realObject,args);        System.out.println("動態代理輸出:附加業務方法計算中....");        return null;    }}

Java的動態代理API要求動態代理的代理處理邏輯,要實現InvocationHandler介面,覆寫invoke()方法。這裡就相當於靜態代理中我們分別實現了login方法和business()方法。

4、測試

/** * www.itzhimei.com */public class Client {    public static void main(String[] args) throws InterruptedException {        User user = new User();        DynamicProxy handler = new DynamicProxy(user);        IUser u = (IUser)Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(),handler);        u.business();    }}

這裡的一個重要方法就是Proxy.newProxyInstance(),這是生成動態代理的類和方法。

5、輸出

動態代理輸出:前置業務方法計算中....使用者類輸出:執行業務方法計算中....使用者類輸出:執行業務方法計算完成動態代理輸出:附加業務方法計算中....

我們看到,動態代理的輸出日誌和handle中定義的invoke是一致的,在實際的業務方法之前和之後執行。

8
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 7個典型用法,沒有人比我更懂SUMPRODUCT