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

本篇文章繼續(xù)介紹Java反射機制,不同的是側(cè)重于介紹動態(tài)代理。動態(tài)代理是代理模式中的一種,是通過Java反射機制來實現(xiàn)的。因此本篇文章先介紹代理模式,然后介紹Java反射機制與動態(tài)代理。

一、代理模式
定義:給某個對象提供一個代理對象,并由代理對象控制對于原對象的訪問,即客戶不直接操控原對象,而是通過代理對象間接地操控原對象。
1、代理模式的理解
代理模式使用代理對象完成用戶請求,屏蔽用戶對真實對象的訪問。現(xiàn)實世界的代理人被授權(quán)執(zhí)行當(dāng)事人的一些事宜,無需當(dāng)事人出面,從第三方的角度看,似乎當(dāng)事人并不存在,因為他只和代理人通信。而事實上代理人是要有當(dāng)事人的授權(quán),并且在核心問題上還需要請示當(dāng)事人。在軟件設(shè)計中,使用代理模式的意圖也很多,比如因為安全原因需要屏蔽客戶端直接訪問真實對象,或者在遠程調(diào)用中需要使用代理類處理遠程方法調(diào)用的技術(shù)細節(jié),也可能為了提升系統(tǒng)性能,對真實對象進行封裝,從而達到延遲加載的目的。
2、代理模式的參與者
代理模式的角色分四種:

圖片.png

主題接口: Subject 是委托對象和代理對象都共同實現(xiàn)的接口,即代理類的所實現(xiàn)的行為接口。Request() 是委托對象和代理對象共同擁有的方法。
目標(biāo)對象:ReaSubject 是原對象,也就是被代理的對象。
代理對象: Proxy 是代理對象,用來封裝真是主題類的代理類。
客戶端 :使用代理類和主題接口完成一些工作。
3、代理模式的分類

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

靜態(tài)代理:代理類是在編譯時就實現(xiàn)好的。也就是說 Java 編譯完成后代理類是一個實際的 class 文件。
動態(tài)代理:代理類是在運行時生成的。也就是說 Java 編譯完之后并沒有實際的 class 文件,而是在運行時動態(tài)生成的類字節(jié)碼,并加載到JVM中。
4、代理模式的實現(xiàn)思路

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

2.代理類和目標(biāo)類分別具體實現(xiàn)接口邏輯。

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

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

5.客戶端想要調(diào)用目標(biāo)對象的行為接口,只能通過代理類來操作。
5、靜態(tài)代理模式的簡單實現(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)對象(RealSubject )以及代理對象(Proxy)都實現(xiàn)了主題接口(Subject)。在代理對象(Proxy)中,通過構(gòu)造函數(shù)傳入目標(biāo)對象(RealSubject ),然后重寫主題接口(Subject)的request()方法,在該方法中調(diào)用目標(biāo)對象(RealSubject )的request()方法,并可以添加一些額外的處理工作在目標(biāo)對象(RealSubject )的request()方法的前后。

代理模式的好處:

假如有這樣的需求,要在某些模塊方法調(diào)用前后加上一些統(tǒng)一的前后處理操作,比如在添加購物車、修改訂單等操作前后統(tǒng)一加上登陸驗證與日志記錄處理,該怎樣實現(xiàn)?首先想到最簡單的就是直接修改源碼,在對應(yīng)模塊的對應(yīng)方法前后添加操作。如果模塊很多,你會發(fā)現(xiàn),修改源碼不僅非常麻煩、難以維護,而且會使代碼顯得十分臃腫。

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

靜態(tài)代理比較簡單,上面的簡單實例就是靜態(tài)代理的應(yīng)用方式,下面介紹本篇文章的主題:動態(tài)代理。
二、Java反射機制與動態(tài)代理

動態(tài)代理的思路和上述思路一致,下面主要講解如何實現(xiàn)。
1、動態(tài)代理介紹

動態(tài)代理是指在運行時動態(tài)生成代理類。即,代理類的字節(jié)碼將在運行時生成并載入當(dāng)前代理的 ClassLoader。與靜態(tài)處理類相比,動態(tài)類有諸多好處。

①不需要為(RealSubject )寫一個形式上完全一樣的封裝類,假如主題接口(Subject)中的方法很多,為每一個接口寫一個代理方法也很麻煩。如果接口有變動,則目標(biāo)對象和代理類都要修改,不利于系統(tǒng)維護;

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

主要涉及兩個類,這兩個類都是java.lang.reflect包下的類,內(nèi)部主要通過反射來實現(xiàn)的。

java.lang.reflect.Proxy: 這是生成代理類的主類,通過 Proxy 類生成的代理類都繼承了 Proxy 類。
Proxy提供了用戶創(chuàng)建動態(tài)代理類和代理對象的靜態(tài)方法,它是所有動態(tài)代理類的父類。

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

Proxy提供了如下兩個方法來創(chuàng)建動態(tài)代理類和動態(tài)代理實例。

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

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

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

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

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

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

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

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

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

InvocationHandler 接口中有方法:

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

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

/**

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

/**

  • 目標(biāo)對象類
    /
    class RealSubject implements Subject{
    public void request(){
    System.out.println("====RealSubject Request====");
    }
    }
    /
    *
  • 代理類的調(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 的不同進行不同的預(yù)處理工作
    System.out.println("====before====");
    //調(diào)用RealSubject中的方法
    Object result = method.invoke(subject, args);
    System.out.println("====after====");
    return result;
    }
    }

可以看到,我們通過newProxyInstance就產(chǎn)生了一個Subject 的實例,即代理類的實例,然后就可以通過Subject .request(),就會調(diào)用InvocationHandler中的invoke()方法,傳入方法Method對象,以及調(diào)用方法的參數(shù),通過Method.invoke調(diào)用RealSubject中的方法的request()方法。同時可以在InvocationHandler中的invoke()方法加入其他執(zhí)行邏輯。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,750評論 2 370

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

  • 本篇文章繼續(xù)介紹Java反射機制,不同的是側(cè)重于介紹動態(tài)代理。動態(tài)代理是代理模式中的一種,是通過Java反射機制來...
    Ruheng閱讀 3,278評論 1 44
  • 一、代理模式 定義:給某個對象提供一個代理對象,并由代理對象控制對于原對象的訪問,即客戶不直接操控原對象,而是通過...
    年少懵懂丶流年夢閱讀 654評論 0 2
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,314評論 11 349
  • 1 場景問題# 1.1 訪問多條數(shù)據(jù)## 考慮這樣一個實際應(yīng)用:要一次性訪問多條數(shù)據(jù)。 這個功能的背景是這樣的;在...
    七寸知架構(gòu)閱讀 3,020評論 1 52
  • 反正這里一個關(guān)注者都沒有,那我就放心寫他和她的故事吧。 今天,她感冒很嚴重,頭暈?zāi)X漲,四肢無力,去打開水的路上,腳...
    純胖胖閱讀 1,369評論 0 0