你真的徹底瞭解Java動態代理嗎?看這篇就夠了

以前講了《零基礎帶你看Spring源碼——IOC控制反轉》,原本打算下一篇講講Srping的AOP的,可是其中會涉及到Java的動態代理,因此先單獨一篇來了解下Java的動態代理究竟是什麼,Java是怎麼實現它的。java

動態代理看起來好像是個什麼高大上的名詞,但其實並無那麼複雜,直接從字面就很容易理解。動態地代理,能夠猜想一下它的含義,在運行時動態地對某些東西代理,代理它作了其餘事情。先不去搞清楚這個動態代理真正的含義,咱們來舉個生動的例子來理解下它到底作了什麼。程序員

一個例子

一個程序員Developer,他會開發code,他調試debug。

程序員有不少分類,其中有Java程序員JavaDeveloper,他會開發Java代碼,會調試Java代碼。

可是呢,有個叫Zack的程序員它在開發以前,會祈禱一下,這樣他開發的代碼就不會有bug。spring

Zack的這種「特異功能」是後天練出來的,並無哪一種程序員有這種特性。雖然咱們也能夠定義一個擁有這樣特性的程序員,可是擁有各類亂七八糟特性的程序千千萬。咱們何時才能定義完,而能保證不漏呢?編程

其實咱們沒有必要去定義他,由於他是後天養成的,咱們應該在這個程序員的成長期去實現這個特性,而不是在他出生以前定義。微信

咱們來看下代碼是怎麼實現的

若是Zack只是一個普通的Java程序員,那麼他的開發結果是
Zack is coding java
Zack is debugging java函數

可是真正的Zack(代理後)
Zack is praying for the code!
Zack is coding java
Zack's have no bug!No need to debug!debug

Proxy.newProxyInstance()

回看下上面是如何使用動態代理的使用。生成一個實例對象,而後用Proxy的newInstance方法對這個實例對象代理生成一個代理對象。

這裏有一個很是關鍵的人,也是比較少人去理解它的。爲何要傳zack的類加載和zack的接口呢?
有沒有留意到zackProxy的類型是Developer接口,而不是一個實現類。由於zack在被代理後生成的對象,並不屬於Developer接口的任何一個實現類。可是它是基於Developer接口和zack的類加載代理出來的。3d

看下newProxyInstance()的接口定義

這三個參數具體的含義來看看註解是怎麼描述的
代理

  • loder,選用的類加載器。由於代理的是zack,因此通常都會用加載zack的類加載器。
  • interfaces,被代理的類所實現的接口,這個接口能夠是多個。
  • h,綁定代理類的一個方法。

loder和interfaces基本就是決定了這個類究竟是個怎麼樣的類。而h是InvocationHandler,決定了這個代理類究竟是多了什麼功能。因此動態代理的內容重點就是這個InvocationHandler。調試

InvocationHandler


根據註解描述可知,InvocationHandler做用就是,當代理對象的本來方法被調用的時候,會綁定執行一個方法,這個方法就是InvocationHandler裏面定義的內容,同時會替代本來方法的結果返回。

InvocationHandler接收三個參數

  • proxy,代理後的實例對象。
  • method,對象被調用方法。
  • args,調用時的參數。

在上面的例子裏,

若是最後的return語句改爲

return method.invoke(proxy, agrs);

invoke的對象不是zack,而是proxy,根據上面的說明猜猜會發生什麼?
是的,會不停地循環調用。由於proxy是代理類的對象,當該對象方法被調用的時候,會觸發InvocationHandler,而InvocationHandler裏面又調用一次proxy裏面的對象,因此會不停地循環調用。而且,proxy對應的方法是沒有實現的。因此是會循環的不停報錯

動態代理的原理

經過上面的講解,相信你們對動態代理的使用理解得比較深入了。那動態代理究竟是怎麼實現的呢,咱們來看看源碼其中關鍵的地方。
在newProxyInstance()發放中有這樣幾段。

其實大概就是把接口複製出來,經過這些接口和類加載器,拿到這個代理類cl。而後經過反射的技術複製拿到代理類的構造函數(這部分代碼在Class類中的getConstructor0方法),最後經過這個構造函數new個一對象出來,同時用InvocationHandler綁定這個對象。

動態代理的使用場景

動態代理的好處咱們從例子就能看出來,它比較靈活,能夠在運行的時候才切入改變類的方法,而不須要預先定義它。

動態代理通常咱們比較少去手寫,但咱們用得其實很是多。在Spring項目中用的註解,例如依賴注入的@Bean、@Autowired,事務註解@Transactional等都有用到,換言之就是Srping的AOP(切面編程)。

這種場景的使用是動態代理最佳的落地點,能夠很是靈活地在某個類,某個方法,某個代碼點上切入咱們想要的內容,就是動態代理其中的內容。因此下一篇咱們來細緻瞭解下Spring的AOP究竟是怎麼使用動態代理的。


更多技術文章、精彩乾貨,請關注
博客:zackku.com
微信公衆號:Zack說碼

相關文章
相關標籤/搜索