Java的代理模式

Java的代理模式

前言

為其他對象提供一個代理以控制對某個對象的訪問。代理類主要負責為委托了(真實對象)預處理消息、過濾消息、傳遞消息給委托類豹休,代理類不現(xiàn)實具體服務,而是利用委托類來完成服務桨吊,并將執(zhí)行結(jié)果封裝處理威根。

其實就是代理類為被代理類預處理消息、過濾消息并在此之后將消息轉(zhuǎn)發(fā)給被代理類视乐,之后還能進行消息的后置處理洛搀。代理類和被代理類通常會存在關聯(lián)關系(即上面提到的持有的被帶離對象的引用),代理類本身不實現(xiàn)服務佑淀,而是通過調(diào)用被代理類中的方法來提供服務留美。

靜態(tài)代理

需求

假如一個班的同學要向老師交班費,都是通過班長把自己的錢轉(zhuǎn)交給老師伸刃。這里谎砾,班長就是學生的代理。

實現(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+"上交班費20元");
        }
    }
    
  • StudentsProxy類激捏,這個類也實現(xiàn)了Person接口设塔,但是還另外持有一個學生類對象,由于實現(xiàn)了Peson接口远舅,

    同時持有一個學生對象闰蛔,那么他可以代理學生類對象執(zhí)行上交班費(執(zhí)行giveMoney()方法)行為。

    public class StudentProxy implements Person {
    
        /**
         *  被代理的學生對象
         */
        Student student;
    
        public StudentProxy(Person person) {
    
            // 只代理學生對象
            if (person.getClass() == Student.class){
                this.student = (Student) person;
            }
        }
    
        @Override
        public void giveMoney() {
            System.out.println("由代理完成上交班費");
            student.giveMoney();
        }
    }
    
  • 測試

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

    結(jié)果:

    由代理完成上交班費
    張三上交班費20元

總結(jié)

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

而且,代碼編譯時就確定了被代理的類是哪一個裁着,這里就是Student類繁涂,所以這進一步是靜態(tài)代碼模式。

代理模式最主要的就是有一個公共接口(Person)二驰,一個具體的類(Student)扔罪,一個代理類(StudentsProxy),代理類持有具體類的實例,代為執(zhí)行具體類實例方法桶雀。

動態(tài)代理

簡介

動態(tài)代理矿酵,代理類并不是在Java代碼中定義的,而是在運行時根據(jù)我們在Java代碼中的“指示”動態(tài)生成的背犯。

相比于靜態(tài)代理坏瘩, 動態(tài)代理的優(yōu)勢在于可以很方便的對代理類的函數(shù)進行統(tǒng)一的處理,而不用修改每個代理類中的方法漠魏。

比如說倔矾,想要在代理類中的每個方法前都加上一個處理方法:

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

實現(xiàn)(還是剛剛的需求)

  • 新建一個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() {
            try {
              //假設數(shù)錢花了一秒時間
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
           System.out.println(name + "上交班費50元");
        }
    }
    
  • 創(chuàng)建自己的InvocationHandler

    public class StuInvocationHandler<T> implements InvocationHandler {
        T target;
    
        public StuInvocationHandler(T target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理執(zhí)行" +method.getName() + "方法");
            //代理過程中插入監(jiān)測方法,計算該方法耗時
            Instant start = Instant.now();
            Object result = method.invoke(target, args);
            Instant end = Instant.now();
            Duration between = Duration.between(start, end);
            System.out.println("方法執(zhí)行的時間:" + between.toMillis());
            return result;
        }
    }
    

測試

public class DynamicProxyTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1哪自、創(chuàng)建一個InvocationHandler對象
        //創(chuàng)建一個實例對象,這個對象是被代理的對象
        Person zhangsan = new Student("張三");
        //創(chuàng)建一個與代理對象相關聯(lián)的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);

        //2禁熏、使用Proxy類的getProxyClass靜態(tài)方法生成一個動態(tài)代理類stuProxyClass
        Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[] {Person.class});

        //3壤巷、獲得stuProxyClass 中一個帶InvocationHandler參數(shù)的構(gòu)造器constructor
        Constructor<?> constructor = stuProxyClass.getConstructor(InvocationHandler.class);

        //4、通過構(gòu)造器constructor來創(chuàng)建一個動態(tài)實例stuProxy
        Person stuProxy = (Person) constructor.newInstance(stuHandler);

        stuProxy.giveMoney();

        // 方式二瞧毙,通過Proxy.newProxyInstance
        Person stuProxy2 = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
        stuProxy.giveMoney();

    }

}

總結(jié)

先想一下胧华,我們創(chuàng)建了一個需要被代理的學生張三寄症,將zhangsan對象傳給了stuHandler中,我們在創(chuàng)建代理對象stuProxy時矩动,將stuHandler作為參數(shù)了的有巧,上面執(zhí)行代理對象的方法(包括giveMoney)都會被替換成執(zhí)行invoke方法,也就是說悲没,最后執(zhí)行的是StuInvocationHandler中的invoke方法篮迎。

動態(tài)代理的優(yōu)勢在于可以很方便的對代理類的所有方法進行統(tǒng)一的處理,而不用修改代理類中的每個方法示姿。

是因為所有被代理執(zhí)行的方法甜橱,都是通過在InvocationHandler中的invoke方法調(diào)用的,所以我們只要在invoke方法中統(tǒng)一處理栈戳,

就可以對所有被代理的方法進行相同的操作了岂傲。

原理

動態(tài)代理具體步驟:

  1. 通過實現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器;
  2. 通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來創(chuàng)建動態(tài)代理類荧琼;
  3. 通過反射機制獲得動態(tài)代理類的構(gòu)造函數(shù)譬胎,其唯一參數(shù)類型是調(diào)用處理器接口類型;
  4. 通過構(gòu)造函數(shù)創(chuàng)建動態(tài)代理類實例命锄,構(gòu)造時調(diào)用處理器對象作為參數(shù)被傳入堰乔。

流程圖

image.png

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市脐恩,隨后出現(xiàn)的幾起案子镐侯,更是在濱河造成了極大的恐慌,老刑警劉巖驶冒,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苟翻,死亡現(xiàn)場離奇詭異,居然都是意外死亡骗污,警方通過查閱死者的電腦和手機崇猫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诅炉,“玉大人,你說我怎么就攤上這事议纯。” “怎么了溢谤?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵憨攒,是天一觀的道長。 經(jīng)常有香客問我鲫构,道長浓恶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任炕吸,我火速辦了婚禮,結(jié)果婚禮上瀑罗,老公的妹妹穿的比我還像新娘摧玫。我一直安慰自己,他們只是感情好诬像,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布降狠。 她就那樣靜靜地躺著,像睡著了一般聂使。 火紅的嫁衣襯著肌膚如雪谬俄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天算灸,我揣著相機與錄音,去河邊找鬼。 笑死贼涩,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播懒浮,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼砚著!你這毒婦竟也來了次伶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤稽穆,失蹤者是張志新(化名)和其女友劉穎冠王,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舌镶,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡柱彻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了餐胀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绒疗。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖骂澄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惕虑,我是刑警寧澤坟冲,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站溃蔫,受9級特大地震影響健提,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伟叛,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一私痹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧统刮,春花似錦紊遵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鞭衩,卻和暖如春学搜,著一層夾襖步出監(jiān)牢的瞬間衬潦,已是汗流浹背讨勤。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留竣灌,地道東北人坯台。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓炬丸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捂人。 傳聞我的和親對象是個殘疾皇子御雕,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353