精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

從頭捋了一遍 Java 代理機(jī)制,收獲頗豐

開(kāi)發(fā) 后端
前文提到,動(dòng)態(tài)代理機(jī)制使用了反射,Spring 中的 AOP 由于使用了動(dòng)態(tài)代理,所以也相當(dāng)于使用了反射機(jī)制。

[[385110]]

本文轉(zhuǎn)載自微信公眾號(hào)「飛天小牛肉」,作者飛天小牛肉。轉(zhuǎn)載本文請(qǐng)聯(lián)系飛天小牛肉公眾號(hào)。

❝這篇文章應(yīng)該在反射那篇結(jié)束后就發(fā)出來(lái)的,結(jié)果搞忘了,現(xiàn)在補(bǔ)上。❞

前文提到,動(dòng)態(tài)代理機(jī)制使用了反射,Spring 中的 AOP 由于使用了動(dòng)態(tài)代理,所以也相當(dāng)于使用了反射機(jī)制。那么,代理是什么?動(dòng)態(tài)代理又是什么?動(dòng)態(tài)代理中是如何使用反射的?全文脈絡(luò)思維導(dǎo)圖如下:


 

 

1. 常規(guī)編碼方式在學(xué)習(xí)代理之前,先回顧以下我們的常規(guī)編碼方式:所有 interface 類型的變量總是通過(guò)向上轉(zhuǎn)型并指向某個(gè)實(shí)例的。

1)首先,定義一個(gè)接口:

  1. public interface SmsService { 
  2.     String send(String message); 

2)然后編寫(xiě)其實(shí)現(xiàn)類:

  1. public class SmsServicseImpl implements SmsService { 
  2.     public String send(String message) { 
  3.         System.out.println("send message:" + message); 
  4.         return message; 
  5.     } 

3)最后創(chuàng)建該實(shí)現(xiàn)類的實(shí)例,轉(zhuǎn)型為接口并調(diào)用:

  1. SmsService s = new SmsServicseImpl(); 
  2. s.send("Java"); 

上述這種方式就是我們通常編寫(xiě)代碼的方式。而代理模式和這種方式有很大的區(qū)別,且看下文。

2. 代理模式概述

簡(jiǎn)單來(lái)說(shuō),代理模式就是 「使用代理對(duì)象來(lái)代替對(duì)真實(shí)對(duì)象的訪問(wèn),這樣就可以在不修改原目標(biāo)對(duì)象的前提下,提供額外的功能操作,擴(kuò)展目標(biāo)對(duì)象的功能。」

代理模式大致有三種角色:

  • Real Subject:真實(shí)類,也就是被代理類、委托類。用來(lái)真正完成業(yè)務(wù)服務(wù)功能;
  • Proxy:代理類。將自身的請(qǐng)求用 Real Subject 對(duì)應(yīng)的功能來(lái)實(shí)現(xiàn),代理類對(duì)象并不真正的去實(shí)現(xiàn)其業(yè)務(wù)功能;
  • Subject:定義 RealSubject 和 Proxy 角色都應(yīng)該實(shí)現(xiàn)的接口。

 

通俗來(lái)說(shuō),「代理模式的主要作用是擴(kuò)展目標(biāo)對(duì)象的功能,比如說(shuō)在目標(biāo)對(duì)象的某個(gè)方法執(zhí)行前后你可以增加一些額外的操作,并且不用修改這個(gè)方法的原有代碼」。如果大家學(xué)過(guò) Spring 的 AOP,一定能夠很好的理解這句話。

舉個(gè)例子:你找了小紅來(lái)幫你向小綠問(wèn)話,小紅就看作是代理我的代理類 Proxy,而你是 Real Subject,因?yàn)樾〖t要傳達(dá)的話其實(shí)是你說(shuō)的。那么你和小紅都需要實(shí)現(xiàn)的接口(Subject)就是說(shuō)話,由于你倆都能說(shuō)話,在外界看來(lái)你倆就是一樣的(滑稽,大家理解就好,不用較真)

 

看到這里,不知道大家能不能理解了為什么委托類和代理類都需要實(shí)現(xiàn)相同的接口?

那是為了保持行為的一致性,在訪問(wèn)者看來(lái)兩者之間就沒(méi)有區(qū)別。這樣,通過(guò)代理類這個(gè)中間層,很好地隱藏和保護(hù)了委托類對(duì)象,能「有效屏蔽外界對(duì)委托類對(duì)象的直接訪問(wèn)」。同時(shí),也可以在代理類上加上額外的操作,比如「小紅在說(shuō)話之前會(huì)跳一段舞,外界就會(huì)覺(jué)得你在說(shuō)話前會(huì)跳一段舞,所以,這就實(shí)現(xiàn)了委托類的功能增強(qiáng)」。

代理模式有靜態(tài)代理和動(dòng)態(tài)代理兩種實(shí)現(xiàn)方式。

3. 靜態(tài)代理

什么是靜態(tài)代理

先來(lái)看靜態(tài)代理的實(shí)現(xiàn)步驟:

1)定義一個(gè)接口(Subject)

2)創(chuàng)建一個(gè)委托類(Real Subject)實(shí)現(xiàn)這個(gè)接口

3)創(chuàng)建一個(gè)代理類(Proxy)同樣實(shí)現(xiàn)這個(gè)接口

4)「將委托類 Real Subject 注入進(jìn)代理類 Proxy」,在代理類的方法中調(diào)用 Real Subject 中的對(duì)應(yīng)方法。這樣的話,我們就可以通過(guò)代理類屏蔽對(duì)目標(biāo)對(duì)象的訪問(wèn),并且可以在目標(biāo)方法執(zhí)行前后做一些自己想做的事情。

從實(shí)現(xiàn)和應(yīng)用角度來(lái)說(shuō),靜態(tài)代理中,我們對(duì)目標(biāo)對(duì)象的每個(gè)方法的增強(qiáng)都是手動(dòng)完成的,非常不靈活(比如接口一旦新增加方法,目標(biāo)對(duì)象和代理對(duì)象都要進(jìn)行修改)且麻煩(需要對(duì)每個(gè)目標(biāo)類都單獨(dú)寫(xiě)一個(gè)代理類)。實(shí)際應(yīng)用場(chǎng)景非常非常少,日常開(kāi)發(fā)幾乎看不到使用靜態(tài)代理的場(chǎng)景。

從 JVM 層面來(lái)說(shuō), 「靜態(tài)代理在編譯時(shí)就將接口、委托類、代理類這些都變成了一個(gè)個(gè)實(shí)際的 .class 文件。」

代碼示例

1)定義發(fā)送短信的接口

  1. public interface SmsService { 
  2.     String send(String message); 

2)創(chuàng)建一個(gè)委托類(Real Subject)實(shí)現(xiàn)這個(gè)接口

  1. public class SmsServiceImpl implements SmsService { 
  2.     public String send(String message) { 
  3.         System.out.println("send message:" + message); 
  4.         return message; 
  5.     } 

3)創(chuàng)建一個(gè)代理類(Proxy)同樣實(shí)現(xiàn)這個(gè)接口

4)將委托類 Real Subject 注入進(jìn)代理類 Proxy,在代理類的方法中調(diào)用 Real Subject 中的對(duì)應(yīng)方法。這樣的話,我們就可以通過(guò)代理類屏蔽對(duì)目標(biāo)對(duì)象的訪問(wèn),并且可以在目標(biāo)方法執(zhí)行前后做一些自己想做的事情。

  1. public class SmsProxy implements SmsService { 
  2.   
  3.     // 將委托類注入進(jìn)代理類 
  4.     private final SmsService smsService; 
  5.  
  6.     public SmsProxy(SmsService smsService) { 
  7.         this.smsService = smsService; 
  8.     } 
  9.  
  10.     @Override 
  11.     public String send(String message) { 
  12.         // 調(diào)用委托類方法之前,我們可以添加自己的操作 
  13.         System.out.println("before method send()"); 
  14.         // 調(diào)用委托類方法 
  15.         smsService.send(message);  
  16.         // 調(diào)用委托類方法之后,我們同樣可以添加自己的操作 
  17.         System.out.println("after method send()"); 
  18.         return null
  19.     } 

那么,如何使用這個(gè)被增強(qiáng)的 send 方法呢?

  1. public class Main { 
  2.     public static void main(String[] args) { 
  3.         SmsService smsService = new SmsServiceImpl(); 
  4.         SmsProxy smsProxy = new SmsProxy(smsService); 
  5.         smsProxy.send("Java"); 
  6.     } 

運(yùn)行上述代碼之后,控制臺(tái)打印出:

  1. before method send() 
  2. send message:java 
  3. after method send() 

從輸出結(jié)果可以看出,我們已經(jīng)增強(qiáng)了委托類 SmsServiceImpl 的 send() 方法。

當(dāng)然,從上述代碼我們也能看出來(lái),靜態(tài)代理存在一定的弊端。假如說(shuō)我們現(xiàn)在新增了一個(gè)委托類實(shí)現(xiàn)了 SmsService 接口,如果我們想要對(duì)這個(gè)委托類進(jìn)行增強(qiáng),就需要重新寫(xiě)一個(gè)代理類,然后注入這個(gè)新的委托類,非常不靈活。也就是說(shuō)靜態(tài)代理是一個(gè)委托了對(duì)應(yīng)一個(gè)代理類,能不能「將代理類做成一個(gè)通用的」呢?為此,動(dòng)態(tài)代理應(yīng)用而生。

4. Java 字節(jié)碼生成框架

在講解動(dòng)態(tài)之前,我們有必要詳細(xì)說(shuō)一下 .class 字節(jié)碼文件這個(gè)東西。動(dòng)態(tài)代理機(jī)制和 Java 字節(jié)碼生成框架息息相關(guān)。

在上文反射中我們提到,一個(gè) Class 類對(duì)應(yīng)一個(gè) .class 字節(jié)碼文件,也就說(shuō)字節(jié)碼文件中存儲(chǔ)了一個(gè)類的全部信息。字節(jié)碼其實(shí)是二進(jìn)制文件,內(nèi)容是只有 JVM 能夠識(shí)別的機(jī)器碼。

解析過(guò)程這樣的:JVM 讀取 .class 字節(jié)碼文件,取出二進(jìn)制數(shù)據(jù),加載到內(nèi)存中,解析字節(jié)碼文件內(nèi)的信息,生成對(duì)應(yīng)的 Class 類對(duì)象:

 

顯然,上述這個(gè)過(guò)程是在編譯期就發(fā)生的。

那么,由于JVM 是通過(guò) .class 字節(jié)碼文件(也就是二進(jìn)制信息)加載類的,如果我們?cè)谶\(yùn)行期遵循 Java 編譯系統(tǒng)組織 .class 字節(jié)碼文件的格式和結(jié)構(gòu),生成相應(yīng)的二進(jìn)制數(shù)據(jù),然后再把這個(gè)二進(jìn)制數(shù)據(jù)加載轉(zhuǎn)換成對(duì)應(yīng)的類。這樣,我們不就完成了在運(yùn)行時(shí)動(dòng)態(tài)的創(chuàng)建一個(gè)類。這個(gè)思想其實(shí)也就是動(dòng)態(tài)代理的思想。

 

在運(yùn)行時(shí)期按照 JVM 規(guī)范對(duì) .class 字節(jié)碼文件的組織規(guī)則,生成對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)。當(dāng)前有很多開(kāi)源框架可以完成這個(gè)功能,如

  • ASM
  • CGLIB
  • Javassist
  • ......

需要注意的是,「CGLIB 是基于 ASM 的」。這里簡(jiǎn)單對(duì)比一下 ASM 和 Javassist:

  • Javassist 源代碼級(jí) API 比 ASM 中實(shí)際的字節(jié)碼操作更容易使用
  • Javassist 在復(fù)雜的字節(jié)碼級(jí)操作上提供了更高級(jí)別的抽象層。Javassist 源代碼級(jí) API 只需要很少的字節(jié)碼知識(shí),甚至不需要任何實(shí)際字節(jié)碼知識(shí),因此實(shí)現(xiàn)起來(lái)更容易、更快。
  • Javassist 使用反射機(jī)制,這使得它比 ASM 慢。

「總的來(lái)說(shuō) ASM 比 Javassist 快得多,并且提供了更好的性能,但是 Javassist 相對(duì)來(lái)說(shuō)更容易使用」,兩者各有千秋。

以 Javassist 為例,我們來(lái)看看這些框架在運(yùn)行時(shí)生成 .class 字節(jié)碼文件的強(qiáng)大能力。

正常來(lái)說(shuō),我們創(chuàng)建一個(gè)類的代碼是這樣的:

  1. package com.samples; 
  2.  
  3. public class Programmer { 
  4.  public void code(){ 
  5.   System.out.println("I'm a Programmer,Just Coding....."); 
  6.  } 

下面通過(guò) Javassist 創(chuàng)建和上面一模一樣的 Programmer 類的字節(jié)碼:

  1. import javassist.ClassPool; 
  2. import javassist.CtClass; 
  3. import javassist.CtMethod; 
  4. import javassist.CtNewMethod; 
  5.  
  6. public class MyGenerator { 
  7.  public static void main(String[] args) throws Exception { 
  8.   ClassPool pool = ClassPool.getDefault(); 
  9.     // 創(chuàng)建 Programmer 類   
  10.   CtClass cc= pool.makeClass("com.samples.Programmer"); 
  11.   // 定義方法 
  12.   CtMethod method = CtNewMethod.make("public void code(){}", cc); 
  13.   // 插入方法代碼 
  14.   method.insertBefore("System.out.println(\"I'm a Programmer,Just Coding.....\");"); 
  15.   cc.addMethod(method); 
  16.   // 保存生成的字節(jié)碼 
  17.   cc.writeFile("d://temp"); 
  18.  } 

通過(guò)反編譯工具打開(kāi) Programmer.class 可以看到以下代碼:

 

恐怖如斯!

5. 什么是動(dòng)態(tài)代理OK,了解了 Java 字節(jié)碼生成框架,可以開(kāi)始學(xué)習(xí)動(dòng)態(tài)代理(Dynamic Proxy)了。

回顧一下靜態(tài)代理,我們把靜態(tài)代理的執(zhí)行過(guò)程抽象為下圖:

 

可以看見(jiàn),代理類無(wú)非是在調(diào)用委托類方法的前后增加了一些操作。委托類的不同,也就導(dǎo)致代理類的不同。

那么為了做一個(gè)通用性的代理類出來(lái),我們把調(diào)用委托類方法的這個(gè)動(dòng)作抽取出來(lái),把它封裝成一個(gè)通用性的處理類,于是就有了動(dòng)態(tài)代理中的 InvocationHandler角色(處理類)。

于是,在代理類和委托類之間就多了一個(gè)處理類的角色,這個(gè)角色主要是「對(duì)代理類調(diào)用委托類方法的這個(gè)動(dòng)作進(jìn)行統(tǒng)一的調(diào)用」,也就是由 InvocationHandler 來(lái)統(tǒng)一處理代理類調(diào)用委托類方法這個(gè)操作。看下圖:

 

「從 JVM 角度來(lái)說(shuō),動(dòng)態(tài)代理是在運(yùn)行時(shí)動(dòng)態(tài)生成 .class 字節(jié)碼文件 ,并加載到 JVM 中的」。這個(gè)我們?cè)?Java 字節(jié)碼生成框架中已經(jīng)提到過(guò)。

雖然動(dòng)態(tài)代理在我們?nèi)粘i_(kāi)發(fā)中使用的相對(duì)較少,但是在框架中的幾乎是必用的一門(mén)技術(shù)。學(xué)會(huì)了動(dòng)態(tài)代理之后,對(duì)于我們理解和學(xué)習(xí)各種框架的原理也非常有幫助,「Spring AOP、RPC 等框架的實(shí)現(xiàn)都依賴了動(dòng)態(tài)代理」。

就 Java 來(lái)說(shuō),動(dòng)態(tài)代理的實(shí)現(xiàn)方式有很多種,比如:

  • JDK 動(dòng)態(tài)代理
  • CGLIB 動(dòng)態(tài)代理
  • Javassit 動(dòng)態(tài)代理
  • ......

下面詳細(xì)講解這三種動(dòng)態(tài)代理機(jī)制。

6. JDK 動(dòng)態(tài)代理機(jī)制

使用步驟

先來(lái)看下 JDK 動(dòng)態(tài)代理機(jī)制的使用步驟:

1)定義一個(gè)接口(Subject)

2)創(chuàng)建一個(gè)委托類(Real Subject)實(shí)現(xiàn)這個(gè)接口

3)創(chuàng)建一個(gè)處理類并實(shí)現(xiàn) InvocationHandler 接口,重寫(xiě)其 invoke 方法(在invoke 方法中利用反射機(jī)制調(diào)用委托類的方法,并自定義一些處理邏輯),并將委托類注入處理類

 

該方法有下面三個(gè)參數(shù):

  • proxy:代理類對(duì)象(見(jiàn)下一步)
  • method:還記得我們?cè)谏掀恼路瓷渲兄v到的 Method.invoke 嗎?就是這個(gè),我們可以通過(guò)它來(lái)調(diào)用委托類的方法(反射)

 

  • args:傳給委托類方法的參數(shù)列表

4)創(chuàng)建代理對(duì)象(Proxy):通過(guò) Proxy.newProxyInstance() 創(chuàng)建委托類對(duì)象的代理對(duì)象

 

這個(gè)方法需要 3 個(gè)參數(shù):

  • 類加載器 ClassLoader
  • 委托類實(shí)現(xiàn)的接口數(shù)組,至少需要傳入一個(gè)接口進(jìn)去
  • 調(diào)用的 InvocationHandler 實(shí)例處理接口方法(也就是第 3 步我們創(chuàng)建的類的實(shí)例)

也就是說(shuō):我們?cè)谕ㄟ^(guò) Proxy 類的 newProxyInstance() 創(chuàng)建的代理對(duì)象在調(diào)用方法的時(shí)候,實(shí)際會(huì)調(diào)用到實(shí)現(xiàn)了 InvocationHandler 接口的處理類的 invoke()方法,可以在 invoke() 方法中自定義處理邏輯,比如在方法執(zhí)行前后做什么事情。

代碼示例

1)定義一個(gè)接口(Subject)

  1. public interface SmsService { 
  2.     String send(String message); 

2)創(chuàng)建一個(gè)委托類(Real Subject)實(shí)現(xiàn)這個(gè)接口

  1. public class SmsServiceImpl implements SmsService { 
  2.     public String send(String message) { 
  3.         System.out.println("send message:" + message); 
  4.         return message; 
  5.     } 

3)創(chuàng)建一個(gè)處理類并實(shí)現(xiàn) InvocationHandler 接口,重寫(xiě)其 invoke 方法(在invoke 方法中利用反射機(jī)制調(diào)用委托類的方法,并自定義一些處理邏輯),并將委托類注入處理類

  1. import java.lang.reflect.InvocationHandler; 
  2. import java.lang.reflect.InvocationTargetException; 
  3. import java.lang.reflect.Method; 
  4.  
  5. public class DebugInvocationHandler implements InvocationHandler { 
  6.      
  7.     // 將委托類注入處理類(這里我們用 Object 代替,方便擴(kuò)展) 
  8.     private final Object target; 
  9.  
  10.     public DebugInvocationHandler(Object target) { 
  11.         this.target = target; 
  12.     } 
  13.   
  14.     // 重寫(xiě) invoke 方法 
  15.     @Override 
  16.     public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { 
  17.         //調(diào)用方法之前,我們可以添加自己的操作 
  18.         System.out.println("before method " + method.getName()); 
  19.         Object result = method.invoke(target, args); 
  20.         //調(diào)用方法之后,我們同樣可以添加自己的操作 
  21.         System.out.println("after method " + method.getName()); 
  22.         return result; 
  23.     } 

4)定義一個(gè)創(chuàng)建代理對(duì)象(Proxy)的工廠類:通過(guò) Proxy.newProxyInstance()創(chuàng)建委托類對(duì)象的代理對(duì)象

  1. public class JdkProxyFactory { 
  2.     public static Object getProxy(Object target) { 
  3.         return Proxy.newProxyInstance( 
  4.                 target.getClass().getClassLoader(), 
  5.                 target.getClass().getInterfaces(), 
  6.                 new DebugInvocationHandler(target) 
  7.         ); 
  8.     } 

5)實(shí)際使用

  1. SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl()); 
  2. smsService.send("Java"); 

運(yùn)行上述代碼之后,控制臺(tái)打印出:

  1. before method send 
  2. send message:Java 
  3. after method send 

 

7. CGLIB 動(dòng)態(tài)代理機(jī)制

使用步驟

「JDK 動(dòng)態(tài)代理有一個(gè)最致命的問(wèn)題是它只能代理實(shí)現(xiàn)了某個(gè)接口的實(shí)現(xiàn)類,并且代理類也只能代理接口中實(shí)現(xiàn)的方法,要是實(shí)現(xiàn)類中有自己私有的方法,而接口中沒(méi)有的話,該方法不能進(jìn)行代理調(diào)用」。

為了解決這個(gè)問(wèn)題,我們可以用 CGLIB 動(dòng)態(tài)代理機(jī)制。

上文也提到過(guò),CGLIB(Code Generation Library)是一個(gè)基于 ASM 的 Java 字節(jié)碼生成框架,它允許我們?cè)谶\(yùn)行時(shí)對(duì)字節(jié)碼進(jìn)行修改和動(dòng)態(tài)生成。原理就是「通過(guò)字節(jié)碼技術(shù)生成一個(gè)子類,并在子類中攔截父類方法的調(diào)用,織入額外的業(yè)務(wù)邏輯」。關(guān)鍵詞大家注意到?jīng)]有,攔截!CGLIB 引入一個(gè)新的角色就是「方法攔截器」MethodInterceptor。和 JDK 中的處理類 InvocationHandler 差不多,也是用來(lái)實(shí)現(xiàn)方法的統(tǒng)一調(diào)用的。看下圖:

 

另外由于 CGLIB 采用「繼承」的方式,所以被代理的類不能被 final 修飾。

很多知名的開(kāi)源框架都使用到了 CGLIB, 例如 「Spring 中的 AOP 模塊中:如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,則默認(rèn)采用 JDK 動(dòng)態(tài)代理,否則采用 CGLIB 動(dòng)態(tài)代理」。

來(lái)看 CGLIB 動(dòng)態(tài)代理的使用步驟:

1)首先創(chuàng)建一個(gè)委托類(Real Subject)

2)創(chuàng)建一個(gè)方法攔截器實(shí)現(xiàn)接口 MethodInterceptor,并重寫(xiě) intercept 方法。intercept 用于攔截并增強(qiáng)委托類的方法(和 JDK 動(dòng)態(tài)代理 InvocationHandler中的 invoke 方法類似)

 

該方法擁有四個(gè)參數(shù):

  • Object var1:委托類對(duì)象
  • Method var2:被攔截的方法(委托類中需要增強(qiáng)的方法)
  • Object[] var3:方法入?yún)?/li>
  • MethodProxy var4:用于調(diào)用委托類的原始方法(底層也是通過(guò)反射機(jī)制,不過(guò)不是 Method.invoke 了,而是使用 MethodProxy.invokeSuper 方法)

 

3)創(chuàng)建代理對(duì)象(Proxy):通過(guò) Enhancer.create() 創(chuàng)建委托類對(duì)象的代理對(duì)象

 

 

也就是說(shuō):我們?cè)谕ㄟ^(guò) Enhancer 類的 create() 創(chuàng)建的代理對(duì)象在調(diào)用方法的時(shí)候,實(shí)際會(huì)調(diào)用到實(shí)現(xiàn)了 MethodInterceptor 接口的處理類的 intercept()方法,可以在 intercept() 方法中自定義處理邏輯,比如在方法執(zhí)行前后做什么事情。

❝可以發(fā)現(xiàn),CGLIB 動(dòng)態(tài)代理機(jī)制和 JDK 動(dòng)態(tài)代理機(jī)制的步驟差不多,CGLIB 動(dòng)態(tài)代理的核心是方法攔截器 MethodInterceptor 和 Enhancer,而 JDK 動(dòng)態(tài)代理的核心是處理類 InvocationHandler 和 Proxy。❞

代碼示例

不同于 JDK 動(dòng)態(tài)代理不需要額外的依賴。CGLIB 是一個(gè)開(kāi)源項(xiàng)目,如果你要使用它的話,需要手動(dòng)添加相關(guān)依賴。

  1. <dependency> 
  2.   <groupId>cglib</groupId> 
  3.   <artifactId>cglib</artifactId> 
  4.   <version>3.3.0</version> 
  5. </dependency> 

1)首先創(chuàng)建一個(gè)委托類(Real Subject)

  1. public class AliSmsService { 
  2.     public String send(String message) { 
  3.         System.out.println("send message:" + message); 
  4.         return message; 
  5.     } 

2)創(chuàng)建一個(gè)方法攔截器實(shí)現(xiàn)接口 MethodInterceptor,并重寫(xiě) intercept 方法

  1. import net.sf.cglib.proxy.MethodInterceptor; 
  2. import net.sf.cglib.proxy.MethodProxy; 
  3. import java.lang.reflect.Method; 
  4.  
  5. public class DebugMethodInterceptor implements MethodInterceptor { 
  6.  
  7.     @Override 
  8.     public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 
  9.         // 調(diào)用方法之前,我們可以添加自己的操作 
  10.         System.out.println("before method " + method.getName()); 
  11.         // 通過(guò)反射調(diào)用委托類的方法 
  12.         Object object = methodProxy.invokeSuper(o, args); 
  13.         // 調(diào)用方法之后,我們同樣可以添加自己的操作 
  14.         System.out.println("after method " + method.getName()); 
  15.         return object; 
  16.     } 
  17.  

3)創(chuàng)建代理對(duì)象(Proxy):通過(guò) Enhancer.create() 創(chuàng)建委托類對(duì)象的代理對(duì)象

  1. import net.sf.cglib.proxy.Enhancer; 
  2.  
  3. public class CglibProxyFactory { 
  4.     public static Object getProxy(Class<?> clazz) { 
  5.         // 創(chuàng)建動(dòng)態(tài)代理增強(qiáng)類 
  6.         Enhancer enhancer = new Enhancer(); 
  7.         // 設(shè)置類加載器 
  8.         enhancer.setClassLoader(clazz.getClassLoader()); 
  9.         // 設(shè)置委托類(設(shè)置父類) 
  10.         enhancer.setSuperclass(clazz); 
  11.         // 設(shè)置方法攔截器 
  12.         enhancer.setCallback(new DebugMethodInterceptor()); 
  13.         // 創(chuàng)建代理類 
  14.         return enhancer.create(); 
  15.     } 

❝從 setSuperclass 我們就能看出,為什么說(shuō) CGLIB 是基于繼承的。❞

4)實(shí)際使用

  1. AliSmsService aliSmsService =  
  2.     (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class); 
  3. aliSmsService.send("Java"); 

運(yùn)行上述代碼之后,控制臺(tái)打印出:

  1. before method send 
  2. send message:Java 
  3. after method send 

JDK 動(dòng)態(tài)代理和 CGLIB 動(dòng)態(tài)代理對(duì)比

1)JDK 動(dòng)態(tài)代理是基于實(shí)現(xiàn)了接口的委托類,通過(guò)接口實(shí)現(xiàn)代理;而 CGLIB 動(dòng)態(tài)代理是基于繼承了委托類的子類,通過(guò)子類實(shí)現(xiàn)代理。

2)JDK 動(dòng)態(tài)代理只能代理實(shí)現(xiàn)了接口的類,且只能增強(qiáng)接口中現(xiàn)有的方法;而 CGLIB 可以代理未實(shí)現(xiàn)任何接口的類。

3)就二者的效率來(lái)說(shuō),大部分情況都是 JDK 動(dòng)態(tài)代理的效率更高,隨著 JDK 版本的升級(jí),這個(gè)優(yōu)勢(shì)更加明顯。

❝提一嘴,常見(jiàn)的還有 「Javassist 動(dòng)態(tài)代理機(jī)制」。和 CGLIB 一樣,作為一個(gè) Java 字節(jié)碼生成框架,Javassist 天生就擁有在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建一個(gè)類的能力,實(shí)現(xiàn)動(dòng)態(tài)代理自然不在話下。Dubbo 就是默認(rèn)使用 Javassit 來(lái)進(jìn)行動(dòng)態(tài)代理的。❞

8. 什么情況下使用動(dòng)態(tài)代理

1)設(shè)計(jì)模式中有一個(gè)設(shè)計(jì)原則是「開(kāi)閉原則」,即「對(duì)修改關(guān)閉,對(duì)擴(kuò)展開(kāi)放」,我們?cè)诠ぷ髦杏袝r(shí)會(huì)接手很多前人的代碼,里面代碼邏輯讓人摸不著頭腦,就很難去下手修改代碼,那么這時(shí)我們就可以通過(guò)代理對(duì)類進(jìn)行增強(qiáng)。

2)我們?cè)谑褂?「RPC 框架」的時(shí)候,框架本身并不能提前知道各個(gè)業(yè)務(wù)方要調(diào)用哪些接口的哪些方法 。那么這個(gè)時(shí)候,就可用通過(guò)動(dòng)態(tài)代理的方式來(lái)建立一個(gè)中間人給客戶端使用,也方便框架進(jìn)行搭建邏輯,某種程度上也是客戶端代碼和框架松耦合的一種表現(xiàn)。

3)「Spring 的 AOP」 機(jī)制同樣也是采用了動(dòng)態(tài)代理,此處不做詳細(xì)討論。

9. 靜態(tài)代理和動(dòng)態(tài)代理對(duì)比

1)「靈活性」 :動(dòng)態(tài)代理更加靈活,不需要必須實(shí)現(xiàn)接口,可以直接代理實(shí)現(xiàn)類,并且可以不需要針對(duì)每個(gè)目標(biāo)類都創(chuàng)建一個(gè)代理類。另外,靜態(tài)代理中,接口一旦新增加方法,目標(biāo)對(duì)象和代理對(duì)象都要進(jìn)行修改,這是非常麻煩的

2)「JVM 層面」 :靜態(tài)代理在編譯時(shí)就將接口、實(shí)現(xiàn)類、代理類這些都變成了一個(gè)個(gè)實(shí)際的 .class 字節(jié)碼文件。而動(dòng)態(tài)代理是在運(yùn)行時(shí)動(dòng)態(tài)生成類字節(jié)碼,并加載到 JVM 中的。

10. 總結(jié)

全部捋一遍下來(lái)還是收獲蠻多的,我感覺(jué)只要理解了字節(jié)碼在編譯期生成還是在運(yùn)行期生成,就差不多能夠把握住靜態(tài)代理和動(dòng)態(tài)代理了。總結(jié)一下靜態(tài)代理和動(dòng)態(tài)代理中的角色:

靜態(tài)代理:

  • Subject:公共接口
  • Real Subject:委托類
  • Proxy:代理類

JDK 動(dòng)態(tài)代理:

  • Subject:公共接口
  • Real Subject:委托類
  • Proxy:代理類
  • 「InvocationHandler」:處理類,統(tǒng)一調(diào)用方法

CGLIB 動(dòng)態(tài)代理:

  • Subject:公共接口
  • Real Subject:委托類
  • Proxy:代理類
  • 「MethodInterceptor」:方法攔截器,統(tǒng)一調(diào)用方法

參考資料《Java 核心技術(shù) - 卷 1 基礎(chǔ)知識(shí) - 第 10 版》

《Thinking In Java(Java 編程思想)- 第 4 版》

JavaGuide:https://snailclimb.gitee.io/javaguide

 

亦山 — Java動(dòng)態(tài)代理機(jī)制詳解(JDK 和CGLIB,Javassist,ASM):https://blog.csdn.net/luanlouis/article/details/24589193

 

責(zé)任編輯:武曉燕 來(lái)源: 飛天小牛肉
相關(guān)推薦

2021-03-11 07:14:01

Epoll原理線程

2020-12-18 06:09:07

Java淺拷貝深拷貝

2021-08-12 10:36:18

order byMySQL數(shù)據(jù)庫(kù)

2023-01-10 19:47:47

Redis原理多線程

2017-12-26 14:17:24

潤(rùn)乾報(bào)表

2021-06-15 07:15:15

Oracle底層explain

2022-01-17 20:59:37

開(kāi)發(fā)group by思路

2024-05-21 08:40:21

分庫(kù)分表源碼

2025-02-13 09:06:27

2021-12-01 07:26:13

IO模型異步

2024-03-26 07:59:32

IO模型多路復(fù)用

2023-09-12 07:31:45

HashMap線程

2024-03-12 08:20:57

零拷貝存儲(chǔ)開(kāi)發(fā)

2021-10-07 20:12:03

MVCC事務(wù)原理

2015-10-10 11:10:24

重敲代碼拷貝粘貼

2019-09-19 08:04:40

網(wǎng)絡(luò)七層模型TCPUDP

2022-02-22 09:16:41

AndroidWindows狀態(tài)欄

2020-02-09 17:30:54

反轉(zhuǎn)鏈表程序員節(jié)點(diǎn)

2022-08-26 10:41:03

指針C語(yǔ)言

2023-08-14 07:49:42

AI訓(xùn)練
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

亚洲天堂视频网| 亚洲第一视频区| 91吃瓜在线观看| 久久久综合视频| 成人免费福利在线| 91精品国产乱码在线观看| 日本成人小视频| 精品国产乱码久久久久久老虎| 激情五月开心婷婷| av免费在线网站| 久久午夜免费电影| 亚洲综合中文字幕68页| 欧美一区二区三区久久久| 亚洲女同另类| 国产午夜精品一区二区三区| 四虎国产精品永久免费观看视频| 深夜成人福利| 亚洲国产精品自拍| 亚洲欧洲国产精品久久| 青青免费在线视频| 国产精品一区一区| 国产精品视频白浆免费视频| 日韩乱码在线观看| 亚洲天堂一区二区三区四区| 亚洲欧洲美洲在线综合| 亚洲图片欧美另类| 伊人久久大香| 欧美在线三级电影| 国产精品无码一区二区在线| caopo在线| 国产精品欧美一区喷水| 美国av一区二区三区| 午夜精品小视频| 精品一区二区久久| 国产精品视频区1| 无码人妻精品一区二区三区蜜桃91| 欧美视频日韩| 欧美成人性色生活仑片| 天堂网中文在线观看| 国产精品一区2区3区| 日韩经典中文字幕在线观看| 中国特级黄色大片| 一区二区三区视频免费视频观看网站 | 欧美高清视频不卡网| 91蝌蚪视频在线观看| 伊人久久综合一区二区| 精品成人久久av| 欧美视频在线播放一区| 中文字幕在线视频久| 精品久久久久久中文字幕| 久操网在线观看| 成人性生交大片免费看在线播放| 亚洲国产精品久久一线不卡| 69sex久久精品国产麻豆| 欧美黑人xx片| 亚洲va中文字幕| 久久综合九色综合88i| 欧美13videosex性极品| 精品高清美女精品国产区| 波多野结衣综合网| 中文字幕在线视频久| 色婷婷亚洲婷婷| 三级a在线观看| 日韩精品第二页| 欧美肥妇毛茸茸| 亚洲av无码久久精品色欲| 一区中文字幕电影| 亚洲精品电影久久久| 久久精品国产亚洲av麻豆| 欧美**vk| 久久久91精品| 国产性一乱一性一伧一色| aa国产精品| 国产成人在线精品| 97在线公开视频| 国产福利一区二区三区在线视频| 91成人免费视频| 午夜成人鲁丝片午夜精品| 久久久电影一区二区三区| 亚洲精品中文字幕在线| 手机在线免费看av| 欧美日韩一区二区在线| 欧美男女交配视频| 中文字幕一区二区三区四区久久| 亚洲第一精品夜夜躁人人爽| 日本激情小视频| 亚洲一区二区三区| 97成人精品视频在线观看| 中文天堂在线播放| 国产精品资源网| 欧美激情国产日韩| gogo在线高清视频| 日韩欧美一区二区三区久久| 天天综合网久久| 伦理一区二区三区| 日韩在线视频播放| 天天爽夜夜爽夜夜爽精品| 日本欧美一区二区| 国产99在线免费| 在线中文资源天堂| 精品国产乱码久久久久久虫虫漫画| 亚洲最大成人在线观看| 黄色美女久久久| 久久人体大胆视频| 精品久久久久久久久久久国产字幕| 国产呦萝稀缺另类资源| 热re99久久精品国99热蜜月| 美足av综合网| 777久久久精品| xxx在线播放| 中文高清一区| 91久久精品www人人做人人爽| 岛国在线大片| 欧美性xxxx极品高清hd直播| 91人妻一区二区三区| 欧美日韩老妇| 欧美尤物巨大精品爽| www.日韩在线观看| 国产精品成人免费在线| 黄色片一级视频| 美女一区二区在线观看| 久久视频免费在线播放| 中文字幕人妻丝袜乱一区三区| 成人午夜视频在线| 亚洲精品天堂成人片av在线播放 | 麻豆国产精品一区| 欧美久久久久| 亚洲free嫩bbb| 色网站在线看| 欧美亚洲愉拍一区二区| 蜜桃传媒一区二区亚洲av| 国内精品久久久久久久影视麻豆 | 精品72久久久久中文字幕| 国产+成+人+亚洲欧洲| 精品人妻久久久久一区二区三区 | 亚洲欧美日韩高清| 日韩黄色一级大片| 不卡在线观看av| 国产美女主播在线| jizz18欧美18| 91极品视频在线| 色wwwwww| 欧美体内谢she精2性欧美| av2014天堂网| 亚洲一级在线| 欧洲精品码一区二区三区免费看| 中国字幕a在线看韩国电影| 亚洲国模精品一区| 久久黄色精品视频| 久久久久亚洲综合| 国产一级不卡毛片| 成人看的视频| 成人久久18免费网站图片| 国产盗摄在线观看| 日韩美一区二区三区| 精品肉丝脚一区二区三区| 成人一区二区三区| 欧美国产亚洲一区| 国模精品一区| 91情侣偷在线精品国产| 在线中文字幕电影| 亚洲第一区中文99精品| 91玉足脚交嫩脚丫在线播放| 91蝌蚪国产九色| 午夜精品在线免费观看| 国产精品二区不卡| av一区二区在线看| 偷拍自拍在线看| 亚洲夜晚福利在线观看| 97在线视频人妻无码| 一区二区三区精品在线观看| 三叶草欧洲码在线| 日本欧美一区二区三区| 国产精品av免费观看| 九色丨蝌蚪丨成人| 国产精品久久久久av免费| 久久bbxx| 日韩精品日韩在线观看| 亚洲天堂视频在线| 亚洲成a人v欧美综合天堂下载| 日本黄色特级片| 另类小说一区二区三区| 免费观看亚洲视频| 精品成人影院| 国产精品欧美久久| 3d欧美精品动漫xxxx无尽| 久久天天躁狠狠躁夜夜爽蜜月| 无码国产精品高潮久久99| 欧美日韩一区二区三区高清| 久久久久久久国产精品毛片| 久久久精品2019中文字幕之3| 中文字幕一区二区三区四| 免费看黄裸体一级大秀欧美| 黄色一级片网址| 要久久电视剧全集免费| 97操在线视频| 在线观看精品| 久久久久久97| 麻豆91在线| 精品偷拍一区二区三区在线看| 91theporn国产在线观看| 午夜精品一区二区三区电影天堂| 日本猛少妇色xxxxx免费网站| 不卡大黄网站免费看| 亚洲 激情 在线| 亚洲永久免费精品| 超碰10000| 久久美女视频| 日韩福利视频| 神马日本精品| 国产精品18毛片一区二区| 欧美一区二区三区婷婷| 日本精品va在线观看| 色呦呦在线播放| 色狠狠久久aa北条麻妃 | 日韩一区av在线| 日本一区二区三区在线观看视频| 日韩一区二区精品| 怡红院成永久免费人全部视频| 婷婷综合在线观看| 久久久久久久久久久久久久久久久| 欧美国产一区视频在线观看| 国产精品jizz| 99re成人精品视频| 美女伦理水蜜桃4| 国产福利91精品一区二区三区| 一级片视频免费观看| 老**午夜毛片一区二区三区| 欧美极品欧美精品欧美| 激情久久中文字幕| 成年在线观看视频| 伊人情人综合网| 国产又大又长又粗又黄| 欧美成人激情| 亚洲最大色综合成人av| 成人6969www免费视频| 欧美13一14另类| 国产一区二区三区91| 欧美极品视频一区二区三区| 女人抽搐喷水高潮国产精品| 国产一区二区三区无遮挡| 国产精品xxxav免费视频| 波多野结衣精品久久| 97一区二区国产好的精华液| 亚洲综合大片69999| 日韩精品一区二区三区中文 | 手机在线精品视频| 亚洲丁香久久久| 日韩美女一级视频| 亚洲欧美在线x视频| 美州a亚洲一视本频v色道| 亚洲欧美综合精品久久成人| lutube成人福利在线观看| 在线观看视频99| 国产在线观看a| 欧美精品一本久久男人的天堂| 在线观看的网站你懂的| 欧美国产在线视频| av免费不卡| 国产成人午夜视频网址| 另类一区二区| 亚洲一区二区三区视频播放| 伊色综合久久之综合久久| 国新精品乱码一区二区三区18| 欧美变态网站| 视频一区二区精品| 天天操夜夜操国产精品| 91视频 - 88av| 国产亚洲毛片| 手机视频在线观看| 国产精品资源网站| 国产精品300页| 中文字幕乱码一区二区免费| 91九色丨porny丨极品女神| 亚洲综合清纯丝袜自拍| 狠狠人妻久久久久久| 欧美高清dvd| 午夜精品一区二区三| 日韩精品极品在线观看| 午夜激情视频在线观看| 久久久久在线观看| 九九九伊在线综合永久| 亚洲一区二区三区久久| 天堂网av成人| 国产手机视频在线观看| 中日韩男男gay无套| 日韩av片专区| 97aⅴ精品视频一二三区| 国产精品一区二区亚洲| 亚洲成人在线观看视频| 中文字幕日本人妻久久久免费| 精品人伦一区二区色婷婷| 欧美大片aaa| 欧美另类极品videosbest最新版本| 国产免费拔擦拔擦8x高清在线人| 国产aⅴ夜夜欢一区二区三区| 国产精品亚洲欧美一级在线| 欧美日韩中文国产一区发布 | 欧美成人网在线| 精品三区视频| 精品国产免费久久久久久尖叫 | 国产探花在线精品一区二区| 蜜臀av.com| 日本视频中文字幕一区二区三区| 国产吃瓜黑料一区二区| 国产精品久久久久久久午夜片| 国产手机在线视频| 日韩一区和二区| h视频网站在线观看| 2019中文字幕在线观看| 日本一区影院| 中国人体摄影一区二区三区| 久久久久久色| 丝袜熟女一区二区三区| 亚洲精品欧美综合四区| 亚洲综合五月天婷婷丁香| 亚洲欧美国产精品专区久久| sqte在线播放| 超碰97在线资源| 99精品视频精品精品视频| www黄色av| 91在线国内视频| www.99re7.com| 日韩午夜av一区| 成人看av片| 国产欧美一区二区三区四区| 国产乱码精品一区二区亚洲 | 亚洲日韩欧美视频| 男人的天堂免费在线视频| 国产福利久久| 黄色亚洲在线| 国内自拍偷拍视频| 亚洲自拍偷拍综合| 午夜精品在线播放| 欧美日韩国产第一页| 美女精品久久| 精品一区二区三区毛片| 极品少妇一区二区三区精品视频| www久久久久久久| 欧美自拍丝袜亚洲| аⅴ资源新版在线天堂| 国产精品成人免费电影| 欧美日韩爱爱| 牛夜精品久久久久久久| 亚洲国产精品二十页| 日本精品入口免费视频| 在线播放亚洲激情| 欧美激情福利| 女同性恋一区二区| 国产激情精品久久久第一区二区| 欧美黑人一级片| 亚洲第一色在线| 自由日本语热亚洲人| 欧美精品久久| 男男视频亚洲欧美| 国产麻豆视频在线观看| 欧美一区日韩一区| 日本资源在线| 久久精品二区| 视频一区视频二区中文| 99久久99久久精品免费| 69堂国产成人免费视频| 视频在线这里都是精品| 国产精品一区视频网站| 先锋影音久久久| 俄罗斯毛片基地| 欧美一级欧美一级在线播放| 久久亚洲导航| 欧美精品成人一区二区在线观看 | 91麻豆精品国产91久久久久 | 亚洲经典一区| 国产伦精品一区二区三区88av| 狠狠躁18三区二区一区| 香蕉视频在线播放| 99re视频在线| 美女爽到呻吟久久久久| 亚洲图片第一页| 欧美va日韩va| avav成人| 欧美亚洲黄色片| 中文字幕成人网| 亚洲国产精品suv| 国产精品扒开腿爽爽爽视频| 在线电影一区二区| 成年人网站免费看| 在线电影一区二区三区| jizz一区二区三区| 亚洲 国产 日韩 综合一区| 国产成人精品一区二区三区四区| 4438国产精品一区二区| 久久久91精品国产| 亚洲最好看的视频| 免费观看一区二区三区| 91搞黄在线观看| 丝袜美腿av在线| 亚洲视频在线二区| 99视频一区二区| 国产区精品在线| 国产成人综合一区二区三区| 你懂的视频一区二区|