你們好,我是小趙,求職的路雖然難,但最終仍是有個着落,我如今進了藏劍山莊任職鑄劍師,不過沒意思,由於活都是低級的活,批量鑄些普通的匕首、短劍之類,一天到晚忙個沒完,這藏劍山莊果真是個大廠,訂單超級多。安全
作着作着我就沒動力了,沒啥技術含量,雖然是計件,但還不如個人打印機業務賺錢來的多,因而我就私底下請一些有空的同事幫我幹活,而我就天天打個卡,而後就在家發展個人打印事業。架構
其實主要緣由呢,仍是由於我暫時不打算離職,先靜觀其變,等待機會,畢竟這是個大企業。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,在此致歉。