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

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

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

一個例子

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

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

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

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

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

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

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

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

Proxy.newProxyInstance()

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

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

看下newProxyInstance()的接口定義

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

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

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

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究竟是怎麼使用動態代理的。

若是以爲還不錯,請關注公衆號:Zack說碼

相關文章
相關標籤/搜索