Java動態代理分析

你的名字

動態代理是JAVA的一大特性。

動態代理的優勢就是實現無浸入式的代碼擴展。
目前動態代理主要分為JAVA自己提供的動態代理和CGLIB類似框架。(本文只說JAVA自己提供的動態代理)

JAVA自帶的動態代理是需要接口的。CGLIB這種則是直接修改字節碼。

在學習Java動態代理之前,我們需要明白靜態代理是怎么實現的,怎么說呢,動態代理是更優雅的實現代理模式。只有了解靜態代理的不足之處,才能更好的運用動態代理。靜態代理對于重復代碼的解決很差勁,每一個override的方法都會有重復代碼。如果一個項目有成百上千個Bean的話,豈不是要造很多靜態代理類么?所以動態代理就出現了。

什么是代理模式?

代理模式的作用是:為其他對象提供一種代理以控制這個對象的訪問。

代理模式有什么好處?

在某些情況下,一個客戶不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介作用。

代理模式一般涉及的角色有?

抽象角色:聲明真實對象和代理對象的共同接口,這樣一來在任何可以使用目標對象的地方都可以使用代理對象。

代理角色:代理對象內部含有目標對象的引用,從而可以在任何時候操作目標對象;代理對象提供一個與目標對象相同的接口,以便可以在任何時候替代目標 對象。代理對象通常在客戶端調用傳遞給目標對象之前或之后,執行某個操作,而不是單傳地將調用傳遞給目標對象,同時,代理對象可以在執行真實對象操作時,附加其他的操作,相當于對真實對象進行封裝。

真實角色:定義了代理對象所代表的目標對象,代理角色鎖代表的真實對象,是我們最終要引用的對象,定義了代理對象所代表的目標對象。

抽象角色:
<pre>public abstract class AbstractObject {
//操作
public abstract void operation();
}</pre>

真實角色:
<pre>
public class RealObject extends AbstractObject {
@Override
public void operation() {
//一些操作
System.out.println("一些操作");
}
}
</pre>

代理角色:
<pre>
public class ProxyObject extends AbstractObject{
RealObject realObject = new RealObject();
@Override
public void operation() {
//調用目標對象之前可以做相關操作
System.out.println("before");
realObject.operation();
//調用目標對象之后可以做相關操作
System.out.println("after");
}
}
</pre>

<pre>public class Client {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    AbstractObject obj = new ProxyObject();
    obj.operation();
}

}
</pre>

以上是靜態代理的實現,接下來才是進入正題了,Java的動態代理。

什么是動態代理?

代理類是在程序運行前不存在,運行時由程序動態生成的代理方式稱為動態代理。
Java提供了動態代理的實現方式,可以在運行時刻動態生成代理類。這種代理方式的一大好處是可以方便對代理類的函數做統一或特殊處理,如記錄所有函數執行時間,所有函數執行前添加驗證判斷,對某個特殊函數進行特殊操作,而不用像靜態代理方式那樣需要修改每個函數。

動態代理實例

實現動態代理包括散步:
1.新建委托類;
2.實現InvocationHandler接口,這是負責連接代理類和委托類的中間類必須實現的接口;
3.通過Proxy類新建代理類對象。

1-新建委托類

<pre>
public interface UserService{
public String getName(int id);
public Integer getAge(int id);
}

public class UserServiceImpl implements UserService {
@Override
public String getName(int id) {
System.out.println("------getName------");
return "Tom";
}

@Override  
public Integer getAge(int id) {  
    System.out.println("------getAge------");  
    return 10;  
}  

}
</pre>

UserService是一個接口,定義了一些函數。
UserServiceImpl是委托類,實現了UserService接口,每個函數只是簡單的輸出字符串。動態代理要求委托類必須實現了某個接口,比如這里委托類UserServiceImpl實現了UserService。

2-實現InvocationHandler接口

<pre>
public class MyInvocationHandler implements InvocationHandler {
private Object target;

MyInvocationHandler() {  
    super();  
}  

MyInvocationHandler(Object target) {  
    super();  
    this.target = target;  
}  

@Override  
public Object invoke(Object o, Method method, Object[] args) throws Throwable {  
    if("getName".equals(method.getName())){  
        System.out.println("++++++before " + method.getName() + "++++++");  
        Object result = method.invoke(target, args);  
        System.out.println("++++++after " + method.getName() + "++++++");  
        return result;  
    }else{  
        Object result = method.invoke(target, args);  
        return result;  
    }  

}  

}
</pre>

target屬性表示委托類對象。
InvocationHandler是負責連接代理類和委托類的中間類必須實現的接口。其中只有一個
<pre>public Object invoke(Object proxy, Method method, Object[] args)</pre>
函數需要去實現,參數:
proxy表示Proxy.newProxyInstance()生成的代理類對象。
method表示代理對象被調用的函數。
args表示代理對象被調用的函數的參數。
調用代理對象的每個函數實際最終都是調用了InvocationHandler的invoke函數。這里我們在invoke實現中添加了一些代碼片。
invoke函數中我們也可以通過method做一些判斷,從而對某些函數特殊處理。

3-通過Proxy類靜態函數生成代理對象

<pre>public class Main1 {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
InvocationHandler invocationHandler = new MyInvocationHandler(userService);
UserService userServiceProxy = (UserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(), invocationHandler);
System.out.println(userServiceProxy.getName(1));
System.out.println(userServiceProxy.getAge(1));
}
} </pre>
這里我們先將委托類對象new UserServiceImpl()
作為MyInvocationHandler
構造函數入參創建invocationHandler
對象;然后通過Proxy.newProxyInstance(…)
函數新建了一個代理對象,實際代理類就是在這時候動態生成的。我們調用該代理對象的函數就會調用到invocationHandler
的invoke
函數(是不是有點類似靜態代理),而invoke
函數實現中調用委托類對象new UserServiceImpl()
相應的 method(是不是有點類似靜態代理)。

<pre>public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)</pre>

loader表示類加載器
interfaces表示委托類的接口,生成代理類時需要實現這些接口
h是InvocationHandler實現類對象,負責連接代理類和委托類的中間類
我們可以這樣理解,如上的動態代理實現實際是雙層的靜態代理,開發者提供了委托類 B,程序動態生成了代理類 A。開發者還需要提供一個實現了InvocationHandler的子類 C,子類 C 連接代理類 A 和委托類 B,它是代理類 A 的委托類,委托類 B 的代理類。用戶直接調用代理類 A 的對象,A 將調用轉發給委托類 C,委托類 C 再將調用轉發給它的委托類 B。

在Android的使用場景

基于 REST 的 Android 端網絡請求框架 Retrofit

作用:簡化網絡請求操作。一般情況下每個網絡請求我們都需要調用一次HttpURLConnection
或者HttpClient
進行請求,或者像 Volley 一樣丟進等待隊列中,Retrofit 極大程度簡化了這些操作,示例代碼如下:
<pre>public interface GitHubService {
@GET("/users/{user}/repos")
List<Repo> listRepos(@Path("user") String user);
}

RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.build();

GitHubService service = restAdapter.create(GitHubService.class);</pre>

以后我們只需直接調用
<pre>List<Repo> repos = service.listRepos("octocat");</pre>

即可開始網絡請求,Retrofit
的原理就是基于動態代理,它同時用到了 注解 的原理,本文不做深入介紹,具體請等待 Retrofit 源碼解析 完成。

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

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,709評論 18 399
  • Java代理和動態代理機制分析和應用 概述 代理是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個...
    丸_子閱讀 3,036評論 6 57
  • 轉自:http://blog.csdn.net/jackfrued/article/details/4492194...
    王帥199207閱讀 8,571評論 3 93
  • 【禪語】 天地澄澈,佛心我心。霧漫的時候,天地間一切都消失了,就剩下一個自己。你就算躲進天之涯,海之角,霧也會如...
    武漢如心閱讀 553評論 1 3
  • 十個女孩里有九個是矯情的有一個是特別矯情的 其實 我有很多個特別的想去看周杰倫的演唱會 所以可以一門心思的打了兩份...
    川枝閱讀 130評論 0 1