1. 程式人生 > >你真的完全瞭解Java動態代理嗎?看這篇就夠了

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

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

動態代理看起來好像是個什麼高大上的名詞,但其實並沒有那麼複雜,直接從字面就很容易理解。動態地代理,可以猜測一下它的含義,在執行時動態地對某些東西代理,代理它做了其他事情。先不去搞清楚這個動態代理真正的含義,我們來舉個生動的例子來理解下它到底做了什麼。

一個例子

一個程式設計師Developer,他會開發code,他除錯debug。程式設計師有很多分類,其中有Java程式設計師JavaDeveloper

,他會開發Java程式碼,會除錯Java程式碼。<!-- more -->但是呢,有個叫Zack的程式設計師它在開發之前,會祈禱一下,這樣他開發的程式碼就不會有bug。

Zack的這種“特異功能”是後天練出來的,並沒有哪種程式設計師有這種特性。雖然我們也可以定義一個擁有這樣特性的程式設計師,但是擁有各種亂七八糟特性的程式千千萬。我們什麼時候才能定義完,而能保證不漏呢?

其實我們沒有必要去定義他,因為他是後天養成的,我們應該在這個程式設計師的成長期去實現這個特性,而不是在他出生之前定義。

我們來看下程式碼是怎麼實現的如果Zack只是一個普通的Java程式設計師,那麼他的開發結果是Zack is coding javaZack is debugging java

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

Proxy.newProxyInstance()

回看下上面是如何使用動態代理的使用。生成一個例項物件,然後用Proxy的newInstance方法對這個例項物件代理生成一個代理物件。這裡有一個非常關鍵的人,也是比較少人去理解它的。為什麼要傳zack的類載入和zack的介面呢?有沒有留意到zackProxy的型別是Developer介面,而不是一個實現類。因為zack在被代理後生成的物件,並不屬於Developer介面的任何一個實現類。但是它是基於Developer介面和zack的類載入代理出來的。

看下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到底是怎麼使用動態代理的。

如果覺得還不錯,請關注公眾號:Zack說碼