Java反射機(jī)制總結(jié)(二)

本篇文章繼續(xù)介紹Java反射機(jī)制,不同的是側(cè)重于介紹動(dòng)態(tài)代理。動(dòng)態(tài)代理是代理模式中的一種监氢,是通過(guò)Java反射機(jī)制來(lái)實(shí)現(xiàn)的。因此本篇文章先介紹代理模式藤违,然后介紹Java反射機(jī)制與動(dòng)態(tài)代理浪腐。

一、代理模式
定義:給某個(gè)對(duì)象提供一個(gè)代理對(duì)象顿乒,并由代理對(duì)象控制對(duì)于原對(duì)象的訪(fǎng)問(wèn)议街,即客戶(hù)不直接操控原對(duì)象,而是通過(guò)代理對(duì)象間接地操控原對(duì)象璧榄。
1特漩、代理模式的理解
代理模式使用代理對(duì)象完成用戶(hù)請(qǐng)求,屏蔽用戶(hù)對(duì)真實(shí)對(duì)象的訪(fǎng)問(wèn)∮塘猓現(xiàn)實(shí)世界的代理人被授權(quán)執(zhí)行當(dāng)事人的一些事宜拾稳,無(wú)需當(dāng)事人出面,從第三方的角度看腊脱,似乎當(dāng)事人并不存在访得,因?yàn)樗缓痛砣送ㄐ拧6聦?shí)上代理人是要有當(dāng)事人的授權(quán),并且在核心問(wèn)題上還需要請(qǐng)示當(dāng)事人悍抑。在軟件設(shè)計(jì)中鳄炉,使用代理模式的意圖也很多,比如因?yàn)榘踩蛐枰帘慰蛻?hù)端直接訪(fǎng)問(wèn)真實(shí)對(duì)象搜骡,或者在遠(yuǎn)程調(diào)用中需要使用代理類(lèi)處理遠(yuǎn)程方法調(diào)用的技術(shù)細(xì)節(jié)拂盯,也可能為了提升系統(tǒng)性能,對(duì)真實(shí)對(duì)象進(jìn)行封裝记靡,從而達(dá)到延遲加載的目的谈竿。
2、代理模式的參與者
代理模式的角色分四種:

圖片.png

主題接口: Subject 是委托對(duì)象和代理對(duì)象都共同實(shí)現(xiàn)的接口摸吠,即代理類(lèi)的所實(shí)現(xiàn)的行為接口空凸。Request() 是委托對(duì)象和代理對(duì)象共同擁有的方法。
目標(biāo)對(duì)象:ReaSubject 是原對(duì)象寸痢,也就是被代理的對(duì)象呀洲。
代理對(duì)象: Proxy 是代理對(duì)象,用來(lái)封裝真是主題類(lèi)的代理類(lèi)啼止。
客戶(hù)端 :使用代理類(lèi)和主題接口完成一些工作道逗。
3、代理模式的分類(lèi)

代理的實(shí)現(xiàn)分為:

靜態(tài)代理:代理類(lèi)是在編譯時(shí)就實(shí)現(xiàn)好的献烦。也就是說(shuō) Java 編譯完成后代理類(lèi)是一個(gè)實(shí)際的 class 文件滓窍。
動(dòng)態(tài)代理:代理類(lèi)是在運(yùn)行時(shí)生成的。也就是說(shuō) Java 編譯完之后并沒(méi)有實(shí)際的 class 文件巩那,而是在運(yùn)行時(shí)動(dòng)態(tài)生成的類(lèi)字節(jié)碼贰您,并加載到JVM中。
4拢操、代理模式的實(shí)現(xiàn)思路

1.代理對(duì)象和目標(biāo)對(duì)象均實(shí)現(xiàn)同一個(gè)行為接口。

2.代理類(lèi)和目標(biāo)類(lèi)分別具體實(shí)現(xiàn)接口邏輯舶替。

3.在代理類(lèi)的構(gòu)造函數(shù)中實(shí)例化一個(gè)目標(biāo)對(duì)象令境。

4.在代理類(lèi)中調(diào)用目標(biāo)對(duì)象的行為接口。

5.客戶(hù)端想要調(diào)用目標(biāo)對(duì)象的行為接口顾瞪,只能通過(guò)代理類(lèi)來(lái)操作舔庶。
5、靜態(tài)代理模式的簡(jiǎn)單實(shí)現(xiàn)

public class ProxyDemo {
public static void main(String args[]){
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}

interface Subject{
void request();
}

class RealSubject implements Subject{
public void request(){
System.out.println("request");
}
}

class Proxy implements Subject{
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
public void request(){
System.out.println("PreProcess");
subject.request();
System.out.println("PostProcess");
}
}

目標(biāo)對(duì)象(RealSubject )以及代理對(duì)象(Proxy)都實(shí)現(xiàn)了主題接口(Subject)陈醒。在代理對(duì)象(Proxy)中惕橙,通過(guò)構(gòu)造函數(shù)傳入目標(biāo)對(duì)象(RealSubject ),然后重寫(xiě)主題接口(Subject)的request()方法钉跷,在該方法中調(diào)用目標(biāo)對(duì)象(RealSubject )的request()方法弥鹦,并可以添加一些額外的處理工作在目標(biāo)對(duì)象(RealSubject )的request()方法的前后。

代理模式的好處:

假如有這樣的需求,要在某些模塊方法調(diào)用前后加上一些統(tǒng)一的前后處理操作彬坏,比如在添加購(gòu)物車(chē)朦促、修改訂單等操作前后統(tǒng)一加上登陸驗(yàn)證與日志記錄處理,該怎樣實(shí)現(xiàn)栓始?首先想到最簡(jiǎn)單的就是直接修改源碼务冕,在對(duì)應(yīng)模塊的對(duì)應(yīng)方法前后添加操作。如果模塊很多幻赚,你會(huì)發(fā)現(xiàn)禀忆,修改源碼不僅非常麻煩、難以維護(hù)落恼,而且會(huì)使代碼顯得十分臃腫箩退。

這時(shí)候就輪到代理模式上場(chǎng)了,它可以在被調(diào)用方法前后加上自己的操作领跛,而不需要更改被調(diào)用類(lèi)的源碼乏德,大大地降低了模塊之間的耦合性,體現(xiàn)了極大的優(yōu)勢(shì)吠昭。

靜態(tài)代理比較簡(jiǎn)單喊括,上面的簡(jiǎn)單實(shí)例就是靜態(tài)代理的應(yīng)用方式,下面介紹本篇文章的主題:動(dòng)態(tài)代理矢棚。
二郑什、Java反射機(jī)制與動(dòng)態(tài)代理

動(dòng)態(tài)代理的思路和上述思路一致,下面主要講解如何實(shí)現(xiàn)蒲肋。
1蘑拯、動(dòng)態(tài)代理介紹

動(dòng)態(tài)代理是指在運(yùn)行時(shí)動(dòng)態(tài)生成代理類(lèi)。即兜粘,代理類(lèi)的字節(jié)碼將在運(yùn)行時(shí)生成并載入當(dāng)前代理的 ClassLoader申窘。與靜態(tài)處理類(lèi)相比,動(dòng)態(tài)類(lèi)有諸多好處孔轴。

①不需要為(RealSubject )寫(xiě)一個(gè)形式上完全一樣的封裝類(lèi)剃法,假如主題接口(Subject)中的方法很多,為每一個(gè)接口寫(xiě)一個(gè)代理方法也很麻煩路鹰。如果接口有變動(dòng)贷洲,則目標(biāo)對(duì)象和代理類(lèi)都要修改,不利于系統(tǒng)維護(hù)晋柱;

②使用一些動(dòng)態(tài)代理的生成方法甚至可以在運(yùn)行時(shí)制定代理類(lèi)的執(zhí)行邏輯优构,從而大大提升系統(tǒng)的靈活性。
2雁竞、動(dòng)態(tài)代理涉及的主要類(lèi)

主要涉及兩個(gè)類(lèi)钦椭,這兩個(gè)類(lèi)都是java.lang.reflect包下的類(lèi),內(nèi)部主要通過(guò)反射來(lái)實(shí)現(xiàn)的。

java.lang.reflect.Proxy: 這是生成代理類(lèi)的主類(lèi)玉凯,通過(guò) Proxy 類(lèi)生成的代理類(lèi)都繼承了 Proxy 類(lèi)势腮。
Proxy提供了用戶(hù)創(chuàng)建動(dòng)態(tài)代理類(lèi)和代理對(duì)象的靜態(tài)方法,它是所有動(dòng)態(tài)代理類(lèi)的父類(lèi)漫仆。

java.lang.reflect.InvocationHandler: 這里稱(chēng)他為"調(diào)用處理器"捎拯,它是一個(gè)接口。當(dāng)調(diào)用動(dòng)態(tài)代理類(lèi)中的方法時(shí)盲厌,將會(huì)直接轉(zhuǎn)接到執(zhí)行自定義的InvocationHandler中的invoke()方法署照。即我們動(dòng)態(tài)生成的代理類(lèi)需要完成的具體內(nèi)容需要自己定義一個(gè)類(lèi),而這個(gè)類(lèi)必須實(shí)現(xiàn) InvocationHandler 接口吗浩,通過(guò)重寫(xiě)invoke()方法來(lái)執(zhí)行具體內(nèi)容建芙。

Proxy提供了如下兩個(gè)方法來(lái)創(chuàng)建動(dòng)態(tài)代理類(lèi)和動(dòng)態(tài)代理實(shí)例。

static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返回代理類(lèi)的java.lang.Class對(duì)象懂扼。第一個(gè)參數(shù)是類(lèi)加載器對(duì)象(即哪個(gè)類(lèi)加載器來(lái)加載這個(gè)代理類(lèi)到 JVM 的方法區(qū))禁荸,第二個(gè)參數(shù)是接口(表明你這個(gè)代理類(lèi)需要實(shí)現(xiàn)哪些接口),第三個(gè)參數(shù)是調(diào)用處理器類(lèi)實(shí)例(指定代理類(lèi)中具體要干什么)阀湿,該代理類(lèi)將實(shí)現(xiàn)interfaces所指定的所有接口赶熟,執(zhí)行代理對(duì)象的每個(gè)方法時(shí)都會(huì)被替換執(zhí)行InvocationHandler對(duì)象的invoke方法。

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回代理類(lèi)實(shí)例陷嘴。參數(shù)與上述方法一致映砖。

對(duì)應(yīng)上述兩種方法創(chuàng)建動(dòng)態(tài)代理對(duì)象的方式:

    //創(chuàng)建一個(gè)InvocationHandler對(duì)象
    InvocationHandler handler = new MyInvocationHandler(.args..);
    //使用Proxy生成一個(gè)動(dòng)態(tài)代理類(lèi)
    Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
    //獲取proxyClass類(lèi)中一個(gè)帶InvocationHandler參數(shù)的構(gòu)造器
    Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
    //調(diào)用constructor的newInstance方法來(lái)創(chuàng)建動(dòng)態(tài)實(shí)例
    RealSubject real = (RealSubject)constructor.newInstance(handler);

    //創(chuàng)建一個(gè)InvocationHandler對(duì)象
    InvocationHandler handler = new MyInvocationHandler(.args..);
    //使用Proxy直接生成一個(gè)動(dòng)態(tài)代理對(duì)象
    RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);

newProxyInstance這個(gè)方法實(shí)際上做了兩件事:第一,創(chuàng)建了一個(gè)新的類(lèi)【代理類(lèi)】灾挨,這個(gè)類(lèi)實(shí)現(xiàn)了Class[] interfaces中的所有接口邑退,并通過(guò)你指定的ClassLoader將生成的類(lèi)的字節(jié)碼加載到JVM中,創(chuàng)建Class對(duì)象劳澄;第二地技,以你傳入的InvocationHandler作為參數(shù)創(chuàng)建一個(gè)代理類(lèi)的實(shí)例并返回。

Proxy 類(lèi)還有一些靜態(tài)方法秒拔,比如:

InvocationHandler getInvocationHandler(Object proxy):獲得代理對(duì)象對(duì)應(yīng)的調(diào)用處理器對(duì)象乓土。

Class getProxyClass(ClassLoader loader, Class[] interfaces):根據(jù)類(lèi)加載器和實(shí)現(xiàn)的接口獲得代理類(lèi)。

InvocationHandler 接口中有方法:

invoke(Object proxy, Method method, Object[] args)
這個(gè)函數(shù)是在代理對(duì)象調(diào)用任何一個(gè)方法時(shí)都會(huì)調(diào)用的溯警,方法不同會(huì)導(dǎo)致第二個(gè)參數(shù)method不同,第一個(gè)參數(shù)是代理對(duì)象(表示哪個(gè)代理對(duì)象調(diào)用了method方法)狡相,第二個(gè)參數(shù)是 Method 對(duì)象(表示哪個(gè)方法被調(diào)用了)梯轻,第三個(gè)參數(shù)是指定調(diào)用方法的參數(shù)。
3尽棕、動(dòng)態(tài)代理模式的簡(jiǎn)單實(shí)現(xiàn)

public class DynamicProxyDemo {
public static void main(String[] args) {
//1.創(chuàng)建目標(biāo)對(duì)象
RealSubject realSubject = new RealSubject();
//2.創(chuàng)建調(diào)用處理器對(duì)象
ProxyHandler handler = new ProxyHandler(realSubject);
//3.動(dòng)態(tài)生成代理對(duì)象
Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(), handler);
//4.通過(guò)代理對(duì)象調(diào)用方法
proxySubject.request();
}
}

/**

  • 主題接口
    */
    interface Subject{
    void request();
    }

/**

  • 目標(biāo)對(duì)象類(lèi)
    /
    class RealSubject implements Subject{
    public void request(){
    System.out.println("====RealSubject Request====");
    }
    }
    /
    *
  • 代理類(lèi)的調(diào)用處理器
    */
    class ProxyHandler implements InvocationHandler{
    private Subject subject;
    public ProxyHandler(Subject subject){
    this.subject = subject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable {
    //定義預(yù)處理的工作喳挑,當(dāng)然你也可以根據(jù) method 的不同進(jìn)行不同的預(yù)處理工作
    System.out.println("====before====");
    //調(diào)用RealSubject中的方法
    Object result = method.invoke(subject, args);
    System.out.println("====after====");
    return result;
    }
    }

可以看到,我們通過(guò)newProxyInstance就產(chǎn)生了一個(gè)Subject 的實(shí)例,即代理類(lèi)的實(shí)例伊诵,然后就可以通過(guò)Subject .request()单绑,就會(huì)調(diào)用InvocationHandler中的invoke()方法,傳入方法Method對(duì)象曹宴,以及調(diào)用方法的參數(shù)搂橙,通過(guò)Method.invoke調(diào)用RealSubject中的方法的request()方法。同時(shí)可以在InvocationHandler中的invoke()方法加入其他執(zhí)行邏輯笛坦。

以上就是代理模式及動(dòng)態(tài)代理的內(nèi)容区转。下篇文章將介紹Java反射機(jī)制與泛型。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末版扩,一起剝皮案震驚了整個(gè)濱河市废离,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌礁芦,老刑警劉巖蜻韭,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異柿扣,居然都是意外死亡肖方,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)窄刘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)窥妇,“玉大人,你說(shuō)我怎么就攤上這事娩践』铘妫” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵翻伺,是天一觀的道長(zhǎng)材泄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)吨岭,這世上最難降的妖魔是什么拉宗? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮辣辫,結(jié)果婚禮上旦事,老公的妹妹穿的比我還像新娘。我一直安慰自己急灭,他們只是感情好姐浮,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著葬馋,像睡著了一般卖鲤。 火紅的嫁衣襯著肌膚如雪肾扰。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,584評(píng)論 1 312
  • 那天蛋逾,我揣著相機(jī)與錄音集晚,去河邊找鬼。 笑死区匣,一個(gè)胖子當(dāng)著我的面吹牛偷拔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沉颂,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼条摸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了铸屉?” 一聲冷哼從身側(cè)響起钉蒲,我...
    開(kāi)封第一講書(shū)人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎彻坛,沒(méi)想到半個(gè)月后顷啼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昌屉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年钙蒙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片间驮。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躬厌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竞帽,到底是詐尸還是另有隱情扛施,我是刑警寧澤,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布屹篓,位于F島的核電站疙渣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏堆巧。R本人自食惡果不足惜妄荔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谍肤。 院中可真熱鬧啦租,春花似錦、人聲如沸荒揣。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)乳附。三九已至内地,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赋除,已是汗流浹背阱缓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留举农,地道東北人荆针。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像颁糟,于是被迫代替她去往敵國(guó)和親航背。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 本篇文章繼續(xù)介紹Java反射機(jī)制棱貌,不同的是側(cè)重于介紹動(dòng)態(tài)代理玖媚。動(dòng)態(tài)代理是代理模式中的一種,是通過(guò)Java反射機(jī)制來(lái)...
    Ruheng閱讀 3,272評(píng)論 1 44
  • 一婚脱、代理模式 定義:給某個(gè)對(duì)象提供一個(gè)代理對(duì)象今魔,并由代理對(duì)象控制對(duì)于原對(duì)象的訪(fǎng)問(wèn),即客戶(hù)不直接操控原對(duì)象障贸,而是通過(guò)...
    年少懵懂丶流年夢(mèng)閱讀 652評(píng)論 0 2
  • 從三月份找實(shí)習(xí)到現(xiàn)在错森,面了一些公司,掛了不少篮洁,但最終還是拿到小米涩维、百度、阿里袁波、京東瓦阐、新浪、CVTE锋叨、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,278評(píng)論 11 349
  • 1 場(chǎng)景問(wèn)題# 1.1 訪(fǎng)問(wèn)多條數(shù)據(jù)## 考慮這樣一個(gè)實(shí)際應(yīng)用:要一次性訪(fǎng)問(wèn)多條數(shù)據(jù)垄分。 這個(gè)功能的背景是這樣的;在...
    七寸知架構(gòu)閱讀 3,011評(píng)論 1 52
  • 反正這里一個(gè)關(guān)注者都沒(méi)有娃磺,那我就放心寫(xiě)他和她的故事吧薄湿。 今天,她感冒很?chē)?yán)重偷卧,頭暈?zāi)X漲豺瘤,四肢無(wú)力,去打開(kāi)水的路上听诸,腳...
    純胖胖閱讀 1,360評(píng)論 0 0