JAVA反射篇之動態(tài)代理

代理模式

代理模式是常用的java設計模式构订,他的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息避矢、過濾消息悼瘾、把消息轉發(fā)給委托類囊榜,以及事后處理消息等。代理類與委托類之間通常會存在關聯(lián)關系亥宿,一個代理類的對象與一個委托類的對象關聯(lián)卸勺,代理類的對象本身并不真正實現(xiàn)服務,而是通過調用委托類的對象的相關方法烫扼,來提供特定的服務曙求。簡單的說就是,我們在訪問實際對象時映企,是通過代理對象來訪問的悟狱,代理模式就是在訪問實際對象時引入一定程度的間接性,因為這種間接性堰氓,可以附加多種用途挤渐。

代理模式結構圖

靜態(tài)代理

靜態(tài)代理的實現(xiàn)比較簡單,代理類通過實現(xiàn)與目標對象相同的接口双絮,并在類中維護一個代理對象浴麻。通過構造器塞入目標對象,賦值給代理對象囤攀,進而執(zhí)行代理對象實現(xiàn)的接口方法软免,并實現(xiàn)前攔截,后攔截等所需的業(yè)務功能焚挠。

靜態(tài)代理在編譯時就已經將接口膏萧,被代理類,代理類等確定下來蝌衔。在程序運行之前向抢,代理類的.class文件就已經生成。

靜態(tài)代理簡單實現(xiàn)

假如一個班的同學要向老師交班費胚委,但是都是通過班長把自己的錢轉交給老師。這里叉信,班長就是學生的代理亩冬。

首先,我們創(chuàng)建一個Person接口硼身。這個接口就是學生(被代理類)硅急,和班長(代理類)的公共接口,他們都有上交班費的行為佳遂。這樣营袜,學生上交班費就可以讓班長來代理執(zhí)行。

public interface Person {
    void giveMoney();
}

Student類實現(xiàn)Person接口丑罪。Student可以具體實施上交班費的動作荚板。

public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    
    @Override
    public void giveMoney() {
       System.out.println(name + "上交班費50元");
    }
}

StudentsProxy類凤壁,這個類也實現(xiàn)了Person接口,但是還另外持有一個學生類對象跪另,由于實現(xiàn)了Peson接口拧抖,同時持有一個學生對象,那么他可以代理學生類對象執(zhí)行上交班費行為免绿。

public class StudentsProxy implements Person{
    //被代理的學生
    Student stu;
    
    public StudentsProxy(Student stu) {
        this.stu = stu;
    }
    
    //代理上交班費唧席,調用被代理學生的上交班費行為
    public void giveMoney() {
        stu.giveMoney();
    }
}

測試

public class StaticProxyTest {
    public static void main(String[] args) {
        //被代理的學生張三,他的班費上交有代理對象monitor(班長)完成
        Person zhangsan = new Student("張三");
        
        //生成代理對象嘲驾,并將張三傳給代理對象
        Person monitor = new StudentsProxy(zhangsan);
        
        //班長代理上交班費
        monitor.giveMoney();
    }
}

運行結果

這里并沒有直接通過張三(被代理對象)來執(zhí)行上交班費的行為淌哟,而是通過班長(代理對象)來代理執(zhí)行了。這就是代理模式辽故。

代理模式最主要的就是有一個公共接口(Person)徒仓,一個具體的類(Student),一個代理類(StudentsProxy),代理類持有具體類的實例榕暇,代為執(zhí)行具體類實例方法蓬衡。上面說到,代理模式就是在訪問實際對象時引入一定程度的間接性彤枢,因為這種間接性狰晚,可以附加多種用途。這里的間接性就是指不直接調用實際對象的方法缴啡,那么我們在代理過程中就可以加上一些其他用途壁晒。就這個例子來說,加入班長在幫張三上交班費之前想要先反映一下張三最近學習有很大進步业栅,通過代理模式很輕松就能辦到:

public class StudentsProxy implements Person{
    //被代理的學生
    Student stu;
    
    public StudentsProxy(Student stu) {
        this.stu = stu;
    }
    
    //代理上交班費秒咐,調用被代理學生的上交班費行為
    public void giveMoney() {
        System.out.println("張三最近學習有進步!");
        stu.giveMoney();
    }
}

運行結果

動態(tài)代理

代理類在程序運行時創(chuàng)建的代理方式被稱為動態(tài)代理碘裕。 我們上面靜態(tài)代理的例子中携取,代理類(studentProxy)是自己定義好的,在程序運行之前就已經編譯完成帮孔。然而動態(tài)代理雷滋,代理類并不是在Java代碼中定義的,而是在運行時根據(jù)我們在Java代碼中的“指示”動態(tài)生成的文兢。相比于靜態(tài)代理晤斩, 動態(tài)代理的優(yōu)勢在于可以很方便的對代理類的函數(shù)進行統(tǒng)一的處理,而不用修改每個代理類中的方法姆坚。

比如說澳泵,想要在每個代理的方法前都加上一個處理方法:

public void giveMoney() {
        //調用被代理方法前加入處理方法
        beforeMethod();
        stu.giveMoney();
    }

這里只有一個giveMoney方法,就寫一次beforeMethod方法兼呵,但是如果除了giveMonney還有很多其他的方法兔辅,那就需要寫很多次beforeMethod方法腊敲,很麻煩而且不夠優(yōu)雅。那看看下面動態(tài)代理如何實現(xiàn)幢妄。

動態(tài)代理簡單實現(xiàn)

在java的java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口兔仰,通過這個類和這個接口可以生成JDK動態(tài)代理類和動態(tài)代理對象。

還是上面班長需要幫學生代交班費的例子蕉鸳。

首先是定義一個Person接口:

public interface Person {
    void giveMoney();
}

創(chuàng)建需要被代理的實際類:

public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    
    @Override
    public void giveMoney() {
       System.out.println(name + "上交班費50元");
    }
}

創(chuàng)建StuInvocationHandler類乎赴,實現(xiàn)InvocationHandler接口,這個類中持有一個被代理對象的實例stu潮尝。InvocationHandler中有一個invoke方法榕吼,所有執(zhí)行代理對象的方法都會被替換成執(zhí)行invoke方法。
然后在invoke方法中執(zhí)行被代理對象stu的相應方法勉失。當然羹蚣,在代理過程中,我們在真正執(zhí)行被代理對象的方法前加入自己其他處理乱凿。

public class StuInvocationHandler implements InvocationHandler {
   //invocationHandler持有的被代理對象
    Student stu;
    
    public StuInvocationHandler(Student stu) {
       this.stu = stu;
    }
    
    /**
     * proxy:代表動態(tài)代理對象
     * method:代表正在執(zhí)行的方法
     * args:代表調用目標方法時傳入的實參
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理執(zhí)行" +method.getName() + "方法");
        Object result = method.invoke(stu, args);
        return result;
    }
}

創(chuàng)建動態(tài)代理對象

public class ProxyTest {
    public static void main(String[] args) {
        //創(chuàng)建一個實例對象顽素,這個對象是被代理的對象
        Person zhangsan = new Student("張三");
        
        //創(chuàng)建一個與代理對象相關聯(lián)的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler(zhangsan);
        
        //創(chuàng)建一個代理對象stuProxy來代理zhangsan,代理對象的每個執(zhí)行方法都會替換執(zhí)行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler)徒蟆;

       //代理執(zhí)行上交班費的方法
        stuProxy.giveMoney();
    }
}

運行結果

動態(tài)代理的優(yōu)勢在于可以很方便的對代理類的函數(shù)進行統(tǒng)一的處理胁出,而不用修改每個代理類中的方法。是因為所有被代理執(zhí)行的方法段审,都是通過在InvocationHandler中的invoke方法調用的全蝶,所以我們只要在invoke方法中統(tǒng)一處理,就可以對所有被代理的方法進行相同的操作了寺枉。

本文大部分引用自:java動態(tài)代理實現(xiàn)與原理詳細分析

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末抑淫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子姥闪,更是在濱河造成了極大的恐慌始苇,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筐喳,死亡現(xiàn)場離奇詭異埂蕊,居然都是意外死亡,警方通過查閱死者的電腦和手機疏唾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來函似,“玉大人槐脏,你說我怎么就攤上這事∑材” “怎么了顿天?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵堂氯,是天一觀的道長。 經常有香客問我牌废,道長咽白,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任鸟缕,我火速辦了婚禮晶框,結果婚禮上,老公的妹妹穿的比我還像新娘懂从。我一直安慰自己授段,他們只是感情好,可當我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布番甩。 她就那樣靜靜地躺著侵贵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缘薛。 梳的紋絲不亂的頭發(fā)上窍育,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天,我揣著相機與錄音宴胧,去河邊找鬼漱抓。 笑死,一個胖子當著我的面吹牛牺汤,可吹牛的內容都是我干的辽旋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼檐迟,長吁一口氣:“原來是場噩夢啊……” “哼补胚!你這毒婦竟也來了?” 一聲冷哼從身側響起追迟,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤溶其,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后敦间,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓶逃,經...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年廓块,在試婚紗的時候發(fā)現(xiàn)自己被綠了厢绝。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡带猴,死狀恐怖昔汉,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情拴清,我是刑警寧澤靶病,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布会通,位于F島的核電站,受9級特大地震影響娄周,放射性物質發(fā)生泄漏涕侈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一煤辨、第九天 我趴在偏房一處隱蔽的房頂上張望裳涛。 院中可真熱鬧,春花似錦掷酗、人聲如沸调违。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽技肩。三九已至,卻和暖如春浮声,著一層夾襖步出監(jiān)牢的瞬間虚婿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工泳挥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留然痊,地道東北人。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓屉符,卻偏偏與公主長得像剧浸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子矗钟,可洞房花燭夜當晚...
    茶點故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內容

  • 關于Java中的動態(tài)代理吨艇,我們首先需要了解的是一種常用的設計模式—代理模式躬它,而對于代理,根據(jù)創(chuàng)建代理類的時間點东涡,又...
    編碼前線閱讀 1,726評論 0 4
  • 原文: Dyanmic Proxy Classes 介紹 一個動態(tài)代理類是實現(xiàn)了多個接口存在于運行時的類冯吓,這樣,一...
    半黑月缺閱讀 943評論 0 0
  • 一疮跑、基本概念 1.什么是代理组贺? 在闡述JDK動態(tài)代理之前,我們很有必要先來弄明白代理的概念祖娘。代理這個詞本身并不是計...
    小李彈花閱讀 16,440評論 2 40
  • 夕陽斜 幾度花謝飛散瓊華 迷離頰睫用初遇時的劍 雕刻著離合煥然了誓約 曾并肩的歲月 從未訴說一樽見夢 相思又為何望...
    夢飲千樽月閱讀 584評論 0 51
  • 歷史唯物論認為,意義并不是什么神秘的整以、虛無飄渺的東西胧辽,相反,它是人的社會存在和社會實踐的產物公黑∫厣蹋”意義體現(xiàn)了人與社會...
    吳黃龍本人閱讀 4,162評論 0 0