關於代理模式的話題有很多,
在開發中經常用到的應該是靜態代理模式,能很好的去耦合。
動態代理是代理模式的另外一種實現。
動態代理的區別在哪裡?
動態代理有什麼好處?
今天我們來分析下這些問題。
回顧靜態代理
之前我們分析過一次靜態代理,
用代理模式優雅地寫代碼
一個典型的代理模式的 Proxy類像下面這樣,
![Java高級編程細節-動態代理-進階高級開發必學技能](http://p2.ttnews.xyz/loading.gif)
對於調用者來說,需要把構造好的實例傳給代理,然後就可以用代理來替代操作真正的實例了。
靜態代理的問題是,
在接口代碼少的情況下一切沒什麼問題,但是當接口增加的時候,
Proxy 類就需要響應的增加接口,比方上面的 Func 接口,
![Java高級編程細節-動態代理-進階高級開發必學技能](http://p2.ttnews.xyz/loading.gif)
剛開始可能只有一個 read()方法,後面慢慢發展到有了 write(),有了 mark(),
隨著接口量的增加, Proxy的維護工作量也在逐步增加。
那麼動態代理能怎麼解決這種問題呢?
動態代理的實現
動態代理的實現步驟基本如下:
· 定義一個公共接口(像 Func)和實現類(像 User),這部分跟靜態代理一樣
· 定義一個 DynamicProxy類實現 InvocationHandler 接口,這個Proxy類似於靜態代理的 Proxy然而不用實現 Func接口
· 調用 Proxy類來構造代理對象
在動態代理的實現中有兩個東西非常重要,一個是 Proxy類,一個是 InvocationHandler接口,都位於 reflect包下,
我們來看第二個步驟所定義的 DynamicProxy是怎樣的吧
它同樣持有了委託對象實例,但是和靜態代理不同,
它並沒有實現委託對象的接口方法,
而只實現了 InvocationHandler的 invoke方法,然後調用了 method.invoke(this.user, objects);
可以留意下 invoke方法,後面我們繼續分析,
這裡再貼一下 Client類的代碼,也就是使用 Proxy的地方,
跟靜態代理不同的地方在於,雖然這裡也需要實例化一個委託類的對象,並傳給 Proxy的構造方法,
但這裡所實例化的是 InvocationHandler對象,而不是 DynamicProxy的對象。
到這裡就完成了一個動態代理的代碼,輸出結果如下
before invoke user method
user read
after invoke user method
比較&分析
· 先來說第二個步驟的 DynamicProxy的實現,
可以發現跟靜態代理不同的地方在於,靜態代理需要實現 Func接口的 read()方法,而動態代理實現的是 InvocationHandler的 invoke方法,
靜態代理需要在不同的接口中去調用 User 接口的不同方法,
而動態代理在invoke被調用的過程中不需要關心需要調用 User 的哪個具體方法,
方法被封裝在 method對象中,而所需要的參數則在 Object[] objects,
直接調用就可以
這裡就意味著即使以後增加了 Func的接口,對於 DynamicProxy來說也不需要增加額外的維護量。
· Proxy.newProxyInstance幹了什麼
在靜態代理裡面,我們會直接用 new StaticProxy(user)構造出來的靜態對象直接操作,
而在動態代理裡面,我們操作的是 Proxy.newProxyInstance所構造出來的動態代理對象 proxyUser,
可能初次接觸動態代理的同學在這裡就概念混亂了,
"難道代理類不是 new DynamicProxy出來的對象嗎?"
其實不是的,如果把 proxyUser的類名打印出來的話,
它會以 $ProxyN的形式存在,N從0開始,這個就是動態代理所生成的真正代理對象,
動態代理的意義就在於這裡,$ProxyN 這個對象是在運行時創建的,
如果用代碼來解釋的話,$ProxyN的代碼會像下面這樣
這個才是真正的代理類。
閱讀更多 菜根譚 的文章