程序:Java代理的幾種方式

程序:Java代理的幾種方式

什麼是代理

什麼是代理呢,其實很好理解,就是不直接訪問目標,而是通過一箇中間層來訪問,就好像下面這樣:

程序:Java代理的幾種方式

Java的靜態代理

舉個例子,如果我們一些水果,比如:香蕉、蘋果等,寫成Java代碼,大概是下面這個樣子:

程序:Java代理的幾種方式

但是吃水果,你要削皮吧,你不能每個水果都寫一個子類,類處理削皮這個事情吧。因此,我們可以做一個代理 ,吃蘋果之前,先把它削皮。 就像下面這樣,把原來的水果包一層:

程序:Java代理的幾種方式

添加了測試類,測試類如下:

程序:Java代理的幾種方式

程序:Java代理的幾種方式

以上就是Java的靜態代理,簡單點說,就是把原來的目標對象包一層,加入新東西再去調用目標本身。 但是如果只是這樣的靜態代理,一個接口,就需要一個代理,實現起來是不是很繁瑣。

Java的動態代理

在Java中,有一個幹這個事情的類,叫做Proxy,可以直接使用反射方式,代理攔截。 先簡單的介紹一下這個類,其實最常用的只有一個靜態方法Proxt.newProxyInstance(),是這樣的:

程序:Java代理的幾種方式

首先我們要實現InvocationHandler,實現其中的invoke方法,在調用目標對象的時候,會先調用到invoke方法,需要實現者在這個方法中,在主動調用被調用者方法。

程序:Java代理的幾種方式

運行一下:

程序:Java代理的幾種方式

程序:Java代理的幾種方式

這個方法,就是生成一個上文中的PeelFruitProxy(當然,我們看到的他名字叫:com.sun.proxy.$Proxy0),動態的生成,避免每次都需要寫,這個也是叫他動態代理的原因,因為我們可以在運行時代理任意類。 很多程序中的AOP就是這樣實現的,但是我們發現一些特點,newProxyInstance()的第二個參數,是一個interfaces的列表,為啥要有這個這個列表呢?

因為我們動態生成的代理類,也需要實現接口,這樣才方便向下轉型,使用其中的方法,不然,生成的類,類名就是com.sun.proxy.$Proxy0這種,並且是在內存中,無法調用生成的方法。 ** 所以,這種動態代理的方法,有一個致命的缺點,那就是被代理的類,必須要實現接口。**

CGLib代理

cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime.

另一個大名鼎鼎的Java代理實現,就是CGLib(Code Generation Library),一個基於ASM的代碼生成框架,可以用他來動態生成類,然後實現對方法的攔截,就可以避開JDK的動態代理, 必須要目標類實現接口的問題了。 也就是說,可以用CGLib來生成上文中的PeelFruitProxy。

簡單介紹一下怎麼用,首先這個CGLib是一個三方的庫,我們要把它依賴進來:

compile 'cglib:cglib:3.2.8'

然後我們來試一試,我們來實現一下上面的代理:

程序:Java代理的幾種方式

運行效果如下:

程序:Java代理的幾種方式

我們看到,實現了同樣的功能,但是,Apple已經不是原來的Apple類了,變成了com.zjiecode.learn.java.proxy.Apple$$EnhancerByCGLIB$$44ade224,沒錯,我們正真使用的是這個類,而不是原來的Apple了,這個類繼承自Apple,最後實現了對Apple類的代理。 這種方式,因為使用的是繼承,所以,無需被代理的類實現接口。當然,他也可以通過接口來實現代理。

總結

  • 第一種代理,就不說了,只適合單一的一個接口的代理,在編譯時就決定好了。
  • 第二、三種代理,都是動態時代理 ,但是我們看到也有差別:
  • JDK的動態代理 ,只能實現接口代理,並且是包裝的被代理對象(類的實例),也就是說,在代理的過程中,有2個對象,一個代理對象,一個目標對象,目標對象被包裝在代理對象裡面。
  • CGLib的代理,是繼承目標對象,生成了一個新的類,然後來實現代理,這樣,在內存中就是有代理對象,沒有目標對象了,使用的是直接繼承的方式
  • 生成代理類是在運行時,有別於javapoet在編譯時生成類。

不過,紙上談來終覺淺,絕知此事要躬行!實踐吧,夥伴們


分享到:


相關文章: