代理模式

殺雞不想用牛刀-用代理

  你們好,我是小趙,求職的路雖然難,但最終仍是有個着落,我如今進了藏劍山莊任職鑄劍師,不過沒意思,由於活都是低級的活,批量鑄些普通的匕首、短劍之類,一天到晚忙個沒完,這藏劍山莊果真是個大廠,訂單超級多。安全

 

  作着作着我就沒動力了,沒啥技術含量,雖然是計件,但還不如個人打印機業務賺錢來的多,因而我就私底下請一些有空的同事幫我幹活,而我就天天打個卡,而後就在家發展個人打印事業。架構

  其實主要緣由呢,仍是由於我暫時不打算離職,先靜觀其變,等待機會,畢竟這是個大企業。ide

 

  如今,是寫日記的時候,我要記錄一下我請同事幹活的過程。函數

 

  首先,同事給我幹活,那就必需要有和我同樣的技能,好比調劑、熔鍊、澆鑄、加工這些鑄劍流程,因此鑄劍的流程應該抽象出來。而同事給我幹活,天知地知,雖然是他乾的活,但這些劍的建立者名字必須是我。優化

 

  思考完畢以後,我畫下了類圖:this

  接下來我就根據這個類圖來寫程序spa

鑄劍流程抽象:代理

public interface IMakeSword {
    //調劑
    void adjust();

    //熔鍊
    void smelt();

    //澆鑄
    void casting();

    //加工
    void process();
} 

 

我:code

public class User implements IMakeSword {
    //名字
    private String name = "";

    public User(String name) {
        this.name = name;
    }
    @Override
    public void adjust() {
        System.out.println(this.name + "正在調劑...");
    }
    @Override
    public void smelt() {
        System.out.println(this.name + "正在熔鍊...");
    }
    @Override
    public void casting() {
        System.out.println(this.name + "正在澆鑄...");
    }
    @Override
    public void process() {
        System.out.println(this.name + "正在加工...");
    }
} 

 

同事:對象

public class Colleague implements IMakeSword{
    private IMakeSword user;

    //構造函數把我傳進來
    public Colleague(IMakeSword user) {
        this.user = user;
    }
    @Override
    public void adjust() {
        this.user.adjust();
    }
    @Override
    public void smelt() {
        this.user.smelt();
    }
    @Override
    public void casting() {
        this.user.casting();
    }
    @Override
    public void process() {
        this.user.process();
    }
}

 

  差很少了,如今個人同事要開始給我幹活了:

    public static void main(String[] args) {
        IMakeSword xiaoZhao = new User("小趙");
        IMakeSword colleague = new Colleague(xiaoZhao);
        colleague.adjust();
        colleague.smelt();
        colleague.casting();
        colleague.process();
    }

 

輸出:

小趙正在調劑...
小趙正在熔鍊...
小趙正在澆鑄...
小趙正在加工...

 

  這個活幹的是很是的好,工資我固然會轉給同事,可是個人工做量在山莊裏還不至於難看。

  隨手一招代理模式,暫時保着這份工做,賺個五險一金。

 

嚴查之下-強制代理

  後來,不知道是哪裏傳出的風聲,山莊高層領導好像知道了什麼,安排了專案小組暗地裏對一些不正之風進行嚴查嚴打。

  我不怕頂風做案,就怕作的不嚴實。

  若是,專案小組假扮個人同事,那我不就嗝屁了嗎?因而,我決定把我和我同事的關係隱藏到內部,不讓人看到,也不接受外面傳入。

 

  來看看我更改後的程序:

鑄劍流程抽象:

public interface IMakeSword {
    //調劑
    void adjust();

    //熔鍊
    void smelt();

    //澆鑄
    void casting();

    //加工
    void process();

    //獲取個人代理
    IMakeSword getProxy();
}

  加一個getProxy方法,獲取個人代理,就是個人同事嘛,增長這個方法是爲了防止外人假冒。

 

我:

public class User implements IMakeSword {
    //名字
    private String name = "";

    //個人同事
    private IMakeSword proxy = null;

    public User(String name) {
        this.name = name;
    }
    @Override
    public void adjust() {
        if(null == this.proxy){
            System.out.println("你好,"+this.name+"在休息。");
            return;
        }
        System.out.println(this.name + "正在調劑...");
    }
    @Override
    public void smelt() {
        if(null == this.proxy){
            System.out.println("你好,"+this.name+"在休息。");
            return;
        }
        System.out.println(this.name + "正在熔鍊...");
    }
    @Override
    public void casting() {
        if(null == this.proxy){
            System.out.println("你好,"+this.name+"在休息。");
            return;
        }
        System.out.println(this.name + "正在澆鑄...");
    }
    @Override
    public void process() {
        if(null == this.proxy){
            System.out.println("你好,"+this.name+"在休息。");
            return;
        }
        System.out.println(this.name + "正在加工...");
    }

    @Override
    public IMakeSword getProxy() {
        //我和同事的小黑屋
        this.proxy = new Colleague(this);
        return this.proxy;
    }
}

在getProxy方法裏,我在小黑屋裏本身聯繫個人同事,而且在每一個動做執行的時候,先檢測我有沒有代理,沒代理的話千萬別亂動。

 

同事:

public class Colleague implements IMakeSword{
    private IMakeSword user;

    //構造函數把我傳進來
    public Colleague(IMakeSword user) {
        this.user = user;
    }
    @Override
    public void adjust() {
        this.user.adjust();
    }
    @Override
    public void smelt() {
        this.user.smelt();
    }
    @Override
    public void casting() {
        this.user.casting();
    }
    @Override
    public void process() {
        this.user.process();
    }

    @Override
    public IMakeSword getProxy() {
        return this;
    }
}

同事類則幾乎沒變化,getProxy方法也不須要的,返回同事本身就能夠了。

 

  來,如今再試一下合做:

    public static void main(String[] args) {
        IMakeSword xiaoZhao = new User("小趙");
        IMakeSword colleague = xiaoZhao.getProxy();
        colleague.adjust();
        colleague.smelt();
        colleague.casting();
        colleague.process();
    }

輸出:

小趙正在調劑...
小趙正在熔鍊...
小趙正在澆鑄...
小趙正在加工...

 

  這是新的合做方式,誰也不知道個人代理是誰。

若是專案小組來假冒同事呢:

    public static void main(String[] args) {
        IMakeSword xiaoZhao = new User("小趙");
        IMakeSword colleague = new Colleague(xiaoZhao);
        colleague.adjust();
        colleague.smelt();
        colleague.casting();
        colleague.process();
    }

輸出:

你好,小趙在休息。
你好,小趙在休息。
你好,小趙在休息。
你好,小趙在休息。

 

  這就是強制代理的手法,代理由本身管理,今後之後,我又能夠高枕無憂了。

 

下降合做門檻-動態代理

  我沒想到,我那個同事不只接個人活,還同時接別人的活,可別人就沒我這麼聰明啊,每天對着他new、new、new!結果沒過幾天就被專案小組給逮着了,直接翻船,同事也被供出來了,被勸退。

  

  隨着我同事的犧牲,山莊的嚴打行動逐漸結束,不久後專案小組解散,緊張的氛圍舒緩而開。

  可是我得重新找合做人啊,私底下接觸了幾個有意向的同事,都以爲個人合做方式太過麻煩,必需要有代理類,就是Colleague類,要實現一籮筐方法,並且建立了類就是留下了證據,未來就有被查到的風險。

  

  在個人一陣子研究以後,發現JDK有一個InvocationHandler接口能夠用,因而乎,我從新寫了一個程序。

 

鑄劍流程抽象:

public interface IMakeSword {
    //調劑
    void adjust();

    //熔鍊
    void smelt();

    //澆鑄
    void casting();

    //加工
    void process();
}

 

我:

public class User implements IMakeSword {
    //名字
    private String name = "";

    public User(String name) {
        this.name = name;
    }
    @Override
    public void adjust() {
        System.out.println(this.name + "正在調劑...");
    }
    @Override
    public void smelt() {
        System.out.println(this.name + "正在熔鍊...");
    }
    @Override
    public void casting() {
        System.out.println(this.name + "正在澆鑄...");
    }
    @Override
    public void process() {
        System.out.println(this.name + "正在加工...");
    }
}

 

動態代理類:

public class ColleagueHandler implements InvocationHandler {
    Object object = null;

    //代理的目標
    public ColleagueHandler(Object object) {
        this.object = object;
    }

    //調用目標方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(this.object,args);
    }
}

 

開始幹活:

    public static void main(String[] args) throws Throwable{
        IMakeSword xiaoZhao = new User("小趙");
        InvocationHandler handler = new ColleagueHandler(xiaoZhao);
        handler.invoke(null,xiaoZhao.getClass().getMethod("adjust", null),null);
        handler.invoke(null,xiaoZhao.getClass().getMethod("smelt", null),null);
        handler.invoke(null,xiaoZhao.getClass().getMethod("casting", null),null);
        handler.invoke(null,xiaoZhao.getClass().getMethod("process", null),null);
    }

輸出:

小趙正在調劑...
小趙正在熔鍊...
小趙正在澆鑄...
小趙正在加工...

 

  很好,程序實現了,從始至終都沒有建立代理類,雖然不少人都會說這是動態代理,但實際上卻不是,這只是反射而已。

  對於一個優秀的架構師來說,這簡直就是渣渣玩法,一籮筐方法名都寫死在外面了,去他大爺的JDK。

  在尋求優化的過程當中,據說JDK還提供了Proxy類,裏面有個newProxyInstance方法用來建立一個對象的代理對象,這個方法總共有3個參數,ClassLoader loader用來指明生成代理對象使用哪一個類裝載器,Class<?>[] interfaces用來指明生成哪一個對象的代理對象,經過接口指定,InvocationHandler h用來指明產生的這個代理對象要作什麼事情。

 

  因而乎,我又舔着大逼臉去使用JDK的接口了。

 

增長一個動態代理類:

public class MyProxy {
    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){
        T newProxyInstance = (T) Proxy.newProxyInstance(loader,interfaces, h);
        return newProxyInstance;
    }
}

 

開始幹活:

    public static void main(String[] args) throws Throwable{
        IMakeSword xiaoZhao = new User("小趙");
        ClassLoader cl = xiaoZhao.getClass().getClassLoader();
        IMakeSword proxy = MyProxy.newProxyInstance(cl,new Class[] {IMakeSword.class}, new ColleagueHandler(xiaoZhao));
        proxy.adjust();
        proxy.smelt();
        proxy.casting();
        proxy.process();
        System.out.println(xiaoZhao.equals(proxy));
    }

輸出:

小趙正在調劑...
小趙正在熔鍊...
小趙正在澆鑄...
小趙正在加工...
false

 

  最後一個比較兩個對象是否相同,證實了proxy對象並非我,但實際上卻又是我在幹活,這才叫動態代理。

 

通知機制-切面

  其實咱們細看之下,已經發現了一個狀況,就是鑄劍相關的類已經和代理解耦了。

  第一條線:IMakeSword接口->User類。

  第二條線:InvocationHandler接口->ColleagueHandler類->MyProxy類。

  動態代理實現代理的職責,業務邏輯負責功能實現。

 

  如今,爲了安全起見,我但願作一件事情,就是每次幹活的時候,要給我發一個通知,讓我知道。

  其實很是的簡單,咱們在建立動態代理的時候發這個通知就好了。

 

  先建立一個通知抽象,由於之後可能會有前置通知、後置通知、各類切入的通知等等,因此通知也是一條獨立發展的線。

 

通知抽象:

public interface IAdvice {
    void exec();
}

 

前置通知:

public class BeforeAdvice implements IAdvice {
    @Override
    public void exec() {
        System.out.println("準備要幹活了");
    }
}

 

動態代理類:

public class MyProxy {
    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){
        new BeforeAdvice().exec();
        T newProxyInstance = (T) Proxy.newProxyInstance(loader,interfaces, h);
        return newProxyInstance;
    }
}

 

開始幹活:

public static void main(String[] args) throws Throwable{
        IMakeSword xiaoZhao = new User("小趙");
        ClassLoader cl = xiaoZhao.getClass().getClassLoader();
        IMakeSword proxy = MyProxy.newProxyInstance(cl,new Class[] {IMakeSword.class}, new ColleagueHandler(xiaoZhao));
        proxy.adjust();
        proxy.smelt();
        proxy.casting();
        proxy.process();
        System.out.println(xiaoZhao.equals(proxy));
    }

 

輸出:

準備要幹活了
小趙正在調劑...
小趙正在熔鍊...
小趙正在澆鑄...
小趙正在加工...
false

 

  若是有的代理須要通知,有的時候不須要通知呢?很簡單,傳個參數,寫個if條件便可,若是不想傳參數呢?通常AOP的作法是使用註解的方式標記切入位置,而後在動態代理類裏面對註解進行判斷。

  獲取註解也可使用反射機制實現,類頭上的註解、方法頭上的註解、參數上的註解均可以獲取,有興趣的朋友能夠自行研究。

 

最後聲明

本故事寫到後面有點怪,緣由是動態代理和靜態代理的使用場景是不一樣的,用同一個故事延續下來致使需求邏輯上出現了一些bug,在此致歉。

相關文章
相關標籤/搜索