<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 17095字,需瀏覽 35分鐘

           ·

          2021-05-10 01:02

          轉(zhuǎn)自:飛天小牛肉

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

          1. 常規(guī)編碼方式

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

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

          public interface SmsService {
              String send(String message);
          }

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

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

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

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

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

          2. 代理模式概述

          簡單來說,代理模式就是 「使用代理對象來代替對真實對象的訪問,這樣就可以在不修改原目標(biāo)對象的前提下,提供額外的功能操作,擴展目標(biāo)對象的功能。」

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

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

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

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

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

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

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

          3. 靜態(tài)代理

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

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

          1)定義一個接口(Subject)

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

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

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

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

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

          代碼示例

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

          public interface SmsService {
              String send(String message);
          }

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

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

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

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

          public class SmsProxy implements SmsService {
           
              // 將委托類注入進代理類
              private final SmsService smsService;

              public SmsProxy(SmsService smsService) {
                  this.smsService = smsService;
              }

              @Override
              public String send(String message) {
                  // 調(diào)用委托類方法之前,我們可以添加自己的操作
                  System.out.println("before method send()");
                  // 調(diào)用委托類方法
                  smsService.send(message); 
                  // 調(diào)用委托類方法之后,我們同樣可以添加自己的操作
                  System.out.println("after method send()");
                  return null;
              }
          }

          那么,如何使用這個被增強的 send 方法呢?

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

          運行上述代碼之后,控制臺打印出:

          before method send()
          send message:java
          after method send()

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

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

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

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

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

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

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

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

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

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

          需要注意的是,「CGLIB 是基于 ASM 的」。這里簡單對比一下 ASM 和 Javassist:

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

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

          以 Javassist 為例,我們來看看這些框架在運行時生成 .class 字節(jié)碼文件的強大能力。

          正常來說,我們創(chuàng)建一個類的代碼是這樣的:

          package com.samples;

          public class Programmer {
           public void code(){
            System.out.println("I'm a Programmer,Just Coding.....");
           }
          }

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

          import javassist.ClassPool;
          import javassist.CtClass;
          import javassist.CtMethod;
          import javassist.CtNewMethod;

          public class MyGenerator {
           public static void main(String[] args) throws Exception {
            ClassPool pool = ClassPool.getDefault();
              // 創(chuàng)建 Programmer 類  
            CtClass cc= pool.makeClass("com.samples.Programmer");
            // 定義方法
            CtMethod method = CtNewMethod.make("public void code(){}", cc);
            // 插入方法代碼
            method.insertBefore("System.out.println(\"I'm a Programmer,Just Coding.....\");");
            cc.addMethod(method);
            // 保存生成的字節(jié)碼
            cc.writeFile("d://temp");
           }
          }

          通過反編譯工具打開 Programmer.class 可以看到以下代碼:

          恐怖如斯!

          5. 什么是動態(tài)代理

          OK,了解了 Java 字節(jié)碼生成框架,可以開始學(xué)習(xí)動態(tài)代理(Dynamic Proxy)了。

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

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

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

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

          「從 JVM 角度來說,動態(tài)代理是在運行時動態(tài)生成 .class 字節(jié)碼文件 ,并加載到 JVM 中的」。這個我們在 Java 字節(jié)碼生成框架中已經(jīng)提到過。

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

          就 Java 來說,動態(tài)代理的實現(xiàn)方式有很多種,比如:

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

          下面詳細講解這三種動態(tài)代理機制。

          6. JDK 動態(tài)代理機制

          使用步驟

          先來看下 JDK 動態(tài)代理機制的使用步驟:

          1)定義一個接口(Subject)

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

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

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

          • proxy:代理類對象(見下一步)

          • method:還記得我們在上篇文章反射中講到的 Method.invoke 嗎?就是這個,我們可以通過它來調(diào)用委托類的方法(反射)

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

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

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

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

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

          代碼示例

          1)定義一個接口(Subject)

          public interface SmsService {
              String send(String message);
          }

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

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

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

          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.InvocationTargetException;
          import java.lang.reflect.Method;

          public class DebugInvocationHandler implements InvocationHandler {
              
              // 將委托類注入處理類(這里我們用 Object 代替,方便擴展)
              private final Object target;

              public DebugInvocationHandler(Object target) {
                  this.target = target;
              }
           
              // 重寫 invoke 方法
              @Override
              public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
                  //調(diào)用方法之前,我們可以添加自己的操作
                  System.out.println("before method " + method.getName());
                  Object result = method.invoke(target, args);
                  //調(diào)用方法之后,我們同樣可以添加自己的操作
                  System.out.println("after method " + method.getName());
                  return result;
              }
          }

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

          public class JdkProxyFactory {
              public static Object getProxy(Object target) {
                  return Proxy.newProxyInstance(
                          target.getClass().getClassLoader(),
                          target.getClass().getInterfaces(),
                          new DebugInvocationHandler(target)
                  );
              }
          }

          5)實際使用

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

          運行上述代碼之后,控制臺打印出:

          before method send
          send message:Java
          after method send

          7. CGLIB 動態(tài)代理機制

          使用步驟

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

          為了解決這個問題,我們可以用 CGLIB 動態(tài)代理機制。

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

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

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

          來看 CGLIB 動態(tài)代理的使用步驟:

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

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

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

          • Object var1:委托類對象

          • Method var2:被攔截的方法(委托類中需要增強的方法)

          • Object[] var3:方法入?yún)?/p>

          • MethodProxy var4:用于調(diào)用委托類的原始方法(底層也是通過反射機制,不過不是 Method.invoke 了,而是使用 MethodProxy.invokeSuper 方法)

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

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

          ?

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

          ?

          代碼示例

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

          <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
          </dependency>

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

          public class AliSmsService {
              public String send(String message) {
                  System.out.println("send message:" + message);
                  return message;
              }
          }

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

          import net.sf.cglib.proxy.MethodInterceptor;
          import net.sf.cglib.proxy.MethodProxy;
          import java.lang.reflect.Method;

          public class DebugMethodInterceptor implements MethodInterceptor {

              @Override
              public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                  // 調(diào)用方法之前,我們可以添加自己的操作
                  System.out.println("before method " + method.getName());
                  // 通過反射調(diào)用委托類的方法
                  Object object = methodProxy.invokeSuper(o, args);
                  // 調(diào)用方法之后,我們同樣可以添加自己的操作
                  System.out.println("after method " + method.getName());
                  return object;
              }

          }

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

          import net.sf.cglib.proxy.Enhancer;

          public class CglibProxyFactory {
              public static Object getProxy(Class<?> clazz) {
                  // 創(chuàng)建動態(tài)代理增強類
                  Enhancer enhancer = new Enhancer();
                  // 設(shè)置類加載器
                  enhancer.setClassLoader(clazz.getClassLoader());
                  // 設(shè)置委托類(設(shè)置父類)
                  enhancer.setSuperclass(clazz);
                  // 設(shè)置方法攔截器
                  enhancer.setCallback(new DebugMethodInterceptor());
                  // 創(chuàng)建代理類
                  return enhancer.create();
              }
          }
          ?

          從 setSuperclass 我們就能看出,為什么說 CGLIB 是基于繼承的。

          ?

          4)實際使用

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

          運行上述代碼之后,控制臺打印出:

          before method send
          send message:Java
          after method send

          JDK 動態(tài)代理和 CGLIB 動態(tài)代理對比

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

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

          3)就二者的效率來說,大部分情況都是 JDK 動態(tài)代理的效率更高,隨著 JDK 版本的升級,這個優(yōu)勢更加明顯。

          ?

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

          ?

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

          1)設(shè)計模式中有一個設(shè)計原則是「開閉原則」,即「對修改關(guān)閉,對擴展開放」,我們在工作中有時會接手很多前人的代碼,里面代碼邏輯讓人摸不著頭腦,就很難去下手修改代碼,那么這時我們就可以通過代理對類進行增強。

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

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

          9. 靜態(tài)代理和動態(tài)代理對比

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

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

          10. 總結(jié)

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

          靜態(tài)代理:

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

          JDK 動態(tài)代理:

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

          CGLIB 動態(tài)代理:

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

          參考資料

          • 《Java 核心技術(shù) - 卷 1 基礎(chǔ)知識 - 第 10 版》
          • 《Thinking In Java(Java 編程思想)- 第 4 版》
          • JavaGuide:https://snailclimb.gitee.io/javaguide
          • 亦山 — Java動態(tài)代理機制詳解(JDK 和CGLIB,Javassist,ASM):https://blog.csdn.net/luanlouis/article/details/24589193

          1、最牛逼的 Java 日志框架,性能無敵,橫掃所有對手!
          2、把Redis當(dāng)作隊列來用,真的合適嗎?
          3、驚呆了,Spring Boot居然這么耗內(nèi)存!你知道嗎?
          4、牛逼哄哄的 BitMap,到底牛逼在哪?
          5、全網(wǎng)最全 Java 日志框架適配方案!還有誰不會?
          6、30個IDEA插件總有一款適合你
          7、Spring中毒太深,離開Spring我居然連最基本的接口都不會寫了

          點分享

          點收藏

          點點贊

          點在看

          瀏覽 60
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  A片一区二区| 久久精品苍井空免费一区二 | www.黄网 | 女人高潮免费视频 | 日本三级天天在干 |