日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Dubbo 源码分析 - 自适应拓展原理

發(fā)布時(shí)間:2025/3/21 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Dubbo 源码分析 - 自适应拓展原理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1.原理

我在上一篇文章中分析了 Dubbo 的 SPI 機(jī)制,Dubbo SPI 是 Dubbo 框架的核心。Dubbo 中的很多拓展都是通過 SPI 機(jī)制進(jìn)行加載的,比如 Protocol、Cluster、LoadBalance 等。有時(shí),有些拓展并非想在框架啟動(dòng)階段被加載,而是希望在拓展方法被調(diào)用時(shí),根據(jù)運(yùn)行時(shí)參數(shù)進(jìn)行加載。這聽起來有些矛盾。拓展未被加載,那么拓展方法就無法被調(diào)用(靜態(tài)方法除外)。拓展方法未被調(diào)用,就無法進(jìn)行加載,這似乎是個(gè)死結(jié)。不過好在也有相應(yīng)的解決辦法,通過代理模式就可以解決這個(gè)問題,這里我們將具有代理功能的拓展稱之為自適應(yīng)拓展。Dubbo 并未直接通過代理模式實(shí)現(xiàn)自適應(yīng)拓展,而是代理代理模式基礎(chǔ)上,封裝了一個(gè)更炫的實(shí)現(xiàn)方式。Dubbo 首先會(huì)為拓展接口生成具有代理功能的代碼,然后通過 javassist 或 jdk 編譯這段代碼,得到 Class 類,最后在通過反射創(chuàng)建代理類。整個(gè)過程比較復(fù)雜、炫麗。如此復(fù)雜的過程最終的目的是為拓展生成代理對(duì)象,但實(shí)際上每個(gè)代理對(duì)象的代理邏輯基本一致,均是從 URL 中獲取欲加載實(shí)現(xiàn)類的名稱。因此,我們完全可以把代理邏輯抽出來,并通過動(dòng)態(tài)代理的方式實(shí)現(xiàn)自適應(yīng)拓展。這樣做的好處顯而易見,方便維護(hù),也方便源碼學(xué)習(xí)者學(xué)習(xí)和調(diào)試代碼。本文將在隨后實(shí)現(xiàn)一個(gè)動(dòng)態(tài)代理版的自適應(yīng)拓展,有興趣的同學(xué)可以繼續(xù)往下讀。

接下來,我們通過一個(gè)示例演示自適應(yīng)拓展類。這個(gè)示例取自 Dubbo 官方文檔,我這里進(jìn)行了一定的拓展。這是一個(gè)與汽車相關(guān)的例子,我們有一個(gè)車輪制造廠接口 WheelMaker:

1 2 3 public interface WheelMaker {Wheel makeWheel(URL url); }

WheelMaker 接口的 Adaptive 實(shí)現(xiàn)類如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class AdaptiveWheelMaker implements WheelMaker {public Wheel makeWheel(URL url) {if (url == null) {throw new IllegalArgumentException("url == null");}// 1.從 URL 中獲取 WheelMaker 名稱String wheelMakerName = url.getParameter("Wheel.maker");if (name == null) {throw new IllegalArgumentException("wheelMakerName == null");}// 2.通過 SPI 加載具體的 WheelMakerWheelMaker wheelMaker = ExtensionLoader.getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName);// 3.調(diào)用目標(biāo)方法return wheelMaker.makeWheel(URL url);} }

AdaptiveWheelMaker 是一個(gè)代理類,它主要做了三件事情:

  • 從 URL 中獲取 WheelMaker 名稱
  • 通過 SPI 加載具體的 WheelMaker
  • 調(diào)用目標(biāo)方法
  • 接下來,我們來看看汽車制造廠 CarMaker 接口與其實(shí)現(xiàn)類。

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public interface CarMaker {Car makeCar(URL url); }public class RaceCarMaker implements CarMaker {WheelMaker wheelMaker;// 通過 setter 注入 AdaptiveWheelMakerpublic setWheelMaker(WheelMaker wheelMaker) {this.wheelMaker = wheelMaker;}public Car makeCar(URL url) {Wheel wheel = wheelMaker.makeWheel(url);return new RaceCar(wheel, ...);} }

    RaceCarMaker 持有一個(gè) WheelMaker 類型從成員變量,在程序啟動(dòng)時(shí),我們可以將 AdaptiveWheelMaker 通過 setter 方法注入到 RaceCarMaker 中。在運(yùn)行時(shí),假設(shè)有這樣一個(gè) URL 類型的參數(shù):

    1 dubbo://192.168.0.101:20880/XxxService?wheel.maker=MichelinWheelMaker

    RaceCarMaker 的 makeCar 方法將上面的 url 作為參數(shù)傳給 AdaptiveWheelMaker 的 makeWheel 方法,makeWheel 方法從 url 中提取 wheel.maker 參數(shù),得到 MichelinWheelMaker。之后再通過 SPI 加載名為 MichelinWheelMaker 的實(shí)現(xiàn)類,得到具體的 WheelMaker 實(shí)例。

    上面這個(gè)示例展示了自適應(yīng)拓展類的核心實(shí)現(xiàn) – 在組件方法被調(diào)用時(shí),通過代理的方式加載指定的實(shí)現(xiàn)類,并調(diào)用被代理的方法。

    經(jīng)過以上說明,大家應(yīng)該搞懂了自適應(yīng)拓展的原理。接下來,我們深入到源碼中,探索自適應(yīng)拓展生成的過程。

    ?2.源碼分析

    在對(duì)自適應(yīng)拓展生成過程進(jìn)行深入分析之前,我們先來看一下與自適應(yīng)拓展息息相關(guān)的一個(gè)注解,即 Adaptive 注解。該注解的定義如下:

    1 2 3 4 5 6 @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Adaptive {String[] value() default {}; }

    從上面的代碼中可知,Adaptive 可注解在類或方法上。注解在類上時(shí),Dubbo 不會(huì)為該類生成代理類。注解上方法(接口方法)上時(shí),Dubbo 會(huì)為為該方法生成代理邏輯。Adaptive 注解在類上的情況很少,在 Dubbo 中,僅有兩個(gè)類被 Adaptive 注解了,分別是 AdaptiveCompiler 和 AdaptiveExtensionFactory。此種情況表示拓展的加載邏輯由人工編碼完成。更多時(shí)候,Adaptive 是注解在接口方法上的,表示拓展的加載邏輯需由框架自動(dòng)生成。Adaptive 注解的地方不同,相應(yīng)的處理邏輯也是不同的。注解在類上時(shí),處理邏輯比較簡(jiǎn)單,本文就不分析了。注解在接口方法上時(shí),處理邏輯較為復(fù)雜,本章將會(huì)重點(diǎn)分析此塊邏輯。接下來,我們從 getAdaptiveExtension 方法進(jìn)行分析。代碼如下:

    ?2.1 獲取自適應(yīng)拓展

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public T getAdaptiveExtension() {// 從緩存中獲取自適應(yīng)拓展Object instance = cachedAdaptiveInstance.get();if (instance == null) { // 緩存未命中if (createAdaptiveInstanceError == null) {synchronized (cachedAdaptiveInstance) {instance = cachedAdaptiveInstance.get();if (instance == null) {try {// 創(chuàng)建自適應(yīng)拓展instance = createAdaptiveExtension();// 設(shè)置拓展到緩存中cachedAdaptiveInstance.set(instance);} catch (Throwable t) {createAdaptiveInstanceError = t;throw new IllegalStateException("...");}}}} else {throw new IllegalStateException("...");}}return (T) instance; }

    getAdaptiveExtension 方法首先會(huì)檢查緩存,緩存未命中,則調(diào)用 createAdaptiveExtension 方法創(chuàng)建自適應(yīng)拓展。下面,我們看一下 createAdaptiveExtension 方法的代碼。

    1 2 3 4 5 6 7 8 private T createAdaptiveExtension() {try {// 獲取自適應(yīng)拓展類,并通過反射實(shí)例化return injectExtension((T) getAdaptiveExtensionClass().newInstance());} catch (Exception e) {throw new IllegalStateException("...");} }

    createAdaptiveExtension 方法代碼比較少,但卻包含了三個(gè)動(dòng)作,分別如下:

  • 調(diào)用 getAdaptiveExtensionClass 方法獲取自適應(yīng)拓展 Class 對(duì)象
  • 通過反射進(jìn)行實(shí)例化
  • 調(diào)用 injectExtension 方法向拓展實(shí)例中注入依賴
  • 前兩個(gè)動(dòng)作比較好理解,第三個(gè)動(dòng)作不好理解,這里簡(jiǎn)單說明一下。injectExtension 方法通過 setter 方法向目標(biāo)對(duì)象中注入依賴,可以看做是一個(gè)簡(jiǎn)單 IOC 的實(shí)現(xiàn)。前面說過,Dubbo 中有兩種類型的自適應(yīng)拓展,一種是手工編碼的,一種是自動(dòng)生成的。手工編碼的 Adaptive 拓展中可能存在著一些依賴,而自動(dòng)生成的 Adaptive 拓展則不會(huì)依賴其他類。這里調(diào)用 injectExtension 方法的目的是為手工編碼的自適應(yīng)拓展注入依賴,這一點(diǎn)需要大家注意一下。關(guān)于 injectExtension 方法,我在上一篇文章中已經(jīng)分析過了,這里不再贅述。接下來,分析 getAdaptiveExtensionClass 方法的邏輯。

    1 2 3 4 5 6 7 8 9 10 private Class<?> getAdaptiveExtensionClass() {// 通過 SPI 獲取所有的拓展類getExtensionClasses();// 檢查緩存,若緩存不為空,則直接返回緩存if (cachedAdaptiveClass != null) {return cachedAdaptiveClass;}// 創(chuàng)建自適應(yīng)拓展類return cachedAdaptiveClass = createAdaptiveExtensionClass(); }

    getAdaptiveExtensionClass 方法也包含了三個(gè)步驟,如下:

  • 調(diào)用 getExtensionClasses 獲取所有的拓展類
  • 檢查緩存,若緩存不為空,則返回緩存
  • 若緩存為空,則調(diào)用 createAdaptiveExtensionClass 創(chuàng)建自適應(yīng)拓展類
  • 這三個(gè)步驟看起來平淡無奇,似乎沒有多講的必要。但是這些平淡無奇的代碼中隱藏了一些細(xì)節(jié),需要說明一下。首先從第一個(gè)步驟說起,getExtensionClasses 這個(gè)方法用于獲取某個(gè)接口的所有實(shí)現(xiàn)類。比如該方法可以獲取 Protocol 接口的 DubboProtocol、HttpProtocol、InjvmProtocol 等實(shí)現(xiàn)類。在獲取實(shí)現(xiàn)類的過程中,如果某個(gè)某個(gè)實(shí)現(xiàn)類被 Adaptive 注解修飾了,那么該類就會(huì)被賦值給 cachedAdaptiveClass 變量。此時(shí),上面步驟中的第二步條件成立(緩存不為空),直接返回 cachedAdaptiveClass 即可。如果所有的實(shí)現(xiàn)類均未被 Adaptive 注解修飾,那么執(zhí)行第三步邏輯,創(chuàng)建自適應(yīng)拓展類。相關(guān)代碼如下:

    1 2 3 4 5 6 7 8 9 private Class<?> createAdaptiveExtensionClass() {// 構(gòu)建自適應(yīng)拓展代碼String code = createAdaptiveExtensionClassCode();ClassLoader classLoader = findClassLoader();// 獲取編譯器實(shí)現(xiàn)類com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();// 編譯代碼,生成 Classreturn compiler.compile(code, classLoader); }

    createAdaptiveExtensionClass 方法用于生成自適應(yīng)拓展類,該方法首先會(huì)生成自適應(yīng)拓展類的源碼,然后通過 Compiler 實(shí)例(Dubbo 默認(rèn)使用 javassist 作為編譯器)編譯源碼,得到代理類 Class 實(shí)例。接下來,我將重點(diǎn)分析代理類代碼生成邏輯。至于代碼編譯的過程,并非本文范疇,這里就不分析了,大家有興趣可以自己看看。下面,我們把目光聚焦在 createAdaptiveExtensionClassCode 方法上。

    ?2.2 自適應(yīng)拓展類代碼生成

    createAdaptiveExtensionClassCode 方法代碼略多,約有兩百行代碼。因此在本節(jié)中,我將會(huì)對(duì)該方法的代碼進(jìn)行拆分分析,以幫助大家更好的理解代碼含義。

    ?2.2.1 Adaptive 注解檢測(cè)

    在生成代理類源碼之前,createAdaptiveExtensionClassCode 方法首先會(huì)通過反射檢測(cè)接口方法是否包含 Adaptive 注解。對(duì)于要生成自適應(yīng)拓展的接口,Dubbo 要求該接口至少有一個(gè)方法被 Adaptive 注解修飾。若不滿足此條件,就會(huì)拋出運(yùn)行時(shí)異常。相關(guān)代碼如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // 通過反射獲取所有的方法 Method[] methods = type.getMethods(); boolean hasAdaptiveAnnotation = false; // 遍歷方法列表 for (Method m : methods) {// 檢測(cè)方法上是否有 Adaptive 注解if (m.isAnnotationPresent(Adaptive.class)) {hasAdaptiveAnnotation = true;break;} }if (!hasAdaptiveAnnotation)// 若所有的方法上均無 Adaptive 注解,則拋出異常throw new IllegalStateException("...");

    ?2.2.2 生成類

    通過 Adaptive 注解檢測(cè)后,即可開始生成代碼。代碼生成的順序與 Java 文件內(nèi)容順序一致,首先會(huì)生成 package 語(yǔ)句,然后生成 import 語(yǔ)句,緊接著生成類名等代碼。整個(gè)邏輯如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // 生成 package 代碼:package + type 所在包 codeBuilder.append("package ").append(type.getPackage().getName()).append(";"); // 生成 import 代碼:import + ExtensionLoader 全限定名 codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";"); // 生成類代碼:public class + type簡(jiǎn)單名稱 + $Adaptive + implements + type全限定名 + { codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {");// ${生成方法}codeBuilder.append("\n}");

    這里,我用 ${…} 占位符代表其他代碼的生成邏輯,該部分邏輯我將在隨后進(jìn)行分析。上面代碼不是很難理解,這里我直接通過一個(gè)例子展示該段代碼所生成的內(nèi)容。以 Dubbo 的 Protocol 接口為例,生成的代碼如下:

    1 2 3 4 5 package com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {// 省略方法代碼 }

    ?2.2.3 生成方法

    一個(gè)方法可以被 Adaptive 注解修飾,也可以不被修飾。這里將未被 Adaptive 注解修飾的方法稱為“無 Adaptive 注解方法”,下面我們先來看看此種方法的代碼生成邏輯是怎樣的。

    ?2.2.3.1 無 Adaptive 注解方法代碼生成

    對(duì)于接口方法,我們可以按照需求標(biāo)注 Adaptive 注解。以 Protocol 接口為例,該接口的 destroy 和 getDefaultPort 未標(biāo)注 Adaptive 注解,其他方法均標(biāo)注了 Adaptive 注解。Dubbo 不會(huì)為沒有標(biāo)注 Adaptive 注解的方法生成代理邏輯,對(duì)于該種類型的方法,僅會(huì)生成一句拋出異常的代碼。生成邏輯如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 for (Method method : methods) {// 省略無關(guān)邏輯Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);StringBuilder code = new StringBuilder(512);// 如果方法上無 Adaptive 注解,則生成 throw new UnsupportedOperationException(...) 代碼if (adaptiveAnnotation == null) {// 生成規(guī)則:// throw new UnsupportedOperationException(// "method " + 方法簽名 + of interface + 全限定接口名 + is not adaptive method!”)code.append("throw new UnsupportedOperationException(\"method ").append(method.toString()).append(" of interface ").append(type.getName()).append(" is not adaptive method!\");");} else {// 省略無關(guān)邏輯}// 省略無關(guān)邏輯 }

    以 Protocol 接口的 destroy 方法為例,上面代碼生成的內(nèi)容如下:

    1 2 throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");

    ?2.2.3.2 獲取 URL 數(shù)據(jù)

    前面說過方法代理邏輯會(huì)從 URL 中提取目標(biāo)拓展的名稱,因此代碼生成邏輯的一個(gè)重要的任務(wù)是從方法的參數(shù)列表獲取其他參數(shù)中獲取 URL 數(shù)據(jù)。舉個(gè)例子說明一下,我們要為 Protocol 接口的 refer 和 export 方法生成代理邏輯。在運(yùn)行時(shí),通過反射得到的方法定義大致如下:

    1 2 Invoker refer(Class<T> arg0, URL arg1) throws RpcException; Exporter export(Invoker<T> arg0) throws RpcException;

    對(duì)于 refer 方法,通過遍歷 refer 的參數(shù)列表即可獲取 URL 數(shù)據(jù),這個(gè)還比較簡(jiǎn)單。對(duì)于 export 方法,獲取 URL 數(shù)據(jù)則要麻煩一些。export 參數(shù)列表中沒有 URL 參數(shù),因此需要從 Invoker 參數(shù)中獲取 URL 數(shù)據(jù)。獲取方式是調(diào)用 Invoker 中可返回 URL 的 getter 方法,比如 getUrl。如果 Invoker 中無相關(guān) getter 方法,此時(shí)則會(huì)拋出異常。整個(gè)邏輯如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 for (Method method : methods) {Class<?> rt = method.getReturnType();Class<?>[] pts = method.getParameterTypes();Class<?>[] ets = method.getExceptionTypes();Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);StringBuilder code = new StringBuilder(512);if (adaptiveAnnotation == null) {// ${無 Adaptive 注解方法代碼生成}} else {int urlTypeIndex = -1;// 遍歷參數(shù)列表,確定 URL 參數(shù)位置for (int i = 0; i < pts.length; ++i) {if (pts[i].equals(URL.class)) {urlTypeIndex = i;break;}}if (urlTypeIndex != -1) { // 參數(shù)列表中存在 URL 參數(shù)// 為 URL 類型參數(shù)生成判空代碼,格式如下:// if (arg + urlTypeIndex == null) // throw new IllegalArgumentException("url == null");String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");",urlTypeIndex);code.append(s);// 為 URL 類型參數(shù)生成賦值代碼,即 URL url = arg1 或 arg2,或 argNs = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex);code.append(s);} else { // 參數(shù)列表中不存在 URL 類型參數(shù)String attribMethod = null;LBL_PTS:// 遍歷方法的參數(shù)類型列表for (int i = 0; i < pts.length; ++i) {// 獲取某一類型參數(shù)的全部方法Method[] ms = pts[i].getMethods();// 遍歷方法列表,尋找可返回 URL 的 getter 方法for (Method m : ms) {String name = m.getName();// 1. 方法名以 get 開頭,或方法名大于3個(gè)字符// 2. 方法的訪問權(quán)限為 public// 3. 方法非靜態(tài)類型// 4. 方法參數(shù)數(shù)量為0// 5. 方法返回值類型為 URLif ((name.startsWith("get") || name.length() > 3)&& Modifier.isPublic(m.getModifiers())&& !Modifier.isStatic(m.getModifiers())&& m.getParameterTypes().length == 0&& m.getReturnType() == URL.class) {urlTypeIndex = i;attribMethod = name;// 結(jié)束 for (int i = 0; i < pts.length; ++i) 循環(huán)break LBL_PTS;}}}if (attribMethod == null) {// 如果所有參數(shù)中均不包含可返回 URL 的 getter 方法,則拋出異常throw new IllegalStateException("...");}// 為包含可返回 URL 的參數(shù)生成判空代碼,格式如下:// if (arg + urlTypeIndex == null) // throw new IllegalArgumentException("參數(shù)全限定名 + argument == null");String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");",urlTypeIndex, pts[urlTypeIndex].getName());code.append(s);// 為 getter 方法返回的 URL 生成判空代碼,格式如下:// if (argN.getter方法名() == null) // throw new IllegalArgumentException(參數(shù)全限定名 + argument getUrl() == null);s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");",urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod);code.append(s);// 生成賦值語(yǔ)句,格式如下:// URL全限定名 url = argN.getter方法名(),比如 // com.alibaba.dubbo.common.URL url = invoker.getUrl();s = String.format("%s url = arg%d.%s();", URL.class.getName(), urlTypeIndex, attribMethod);code.append(s);}// 省略無關(guān)代碼}// 省略無關(guān)代碼 }

    上面代碼有點(diǎn)多,但并不是很難看懂。這段代碼主要是為了獲取 URL 數(shù)據(jù),并為之生成判空和賦值代碼。以 Protocol 的 refer 和 export 方法為例,上面代碼會(huì)為它們生成如下內(nèi)容(代碼已格式化):

    1 2 3 4 5 6 7 8 9 10 11 refer: if (arg1 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg1;export: if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null"); com.alibaba.dubbo.common.URL url = arg0.getUrl();

    ?2.2.3.3 獲取 Adaptive 注解值

    Adaptive 注解值 value 類型為 String[],可填寫多個(gè)值,默認(rèn)情況下為空數(shù)組。若 value 為非空數(shù)組,直接獲取數(shù)組內(nèi)容即可。若 value 為空數(shù)組,則需進(jìn)行額外處理。處理的過程是將類名轉(zhuǎn)換為字符數(shù)組,然后遍歷字符數(shù)組,并將字符加入到 StringBuilder 中。若字符為大寫字母,則向 StringBuilder 中添加點(diǎn)號(hào),隨后將字符變?yōu)樾懘嫒?StringBuilder 中。比如 LoadBalance 經(jīng)過處理后,得到 load.balance。

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 for (Method method : methods) {Class<?> rt = method.getReturnType();Class<?>[] pts = method.getParameterTypes();Class<?>[] ets = method.getExceptionTypes();Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);StringBuilder code = new StringBuilder(512);if (adaptiveAnnotation == null) {// ${無 Adaptive 注解方法代碼生成}} else {// ${獲取 URL 數(shù)據(jù)}String[] value = adaptiveAnnotation.value();// value 為空數(shù)組if (value.length == 0) {// 獲取類名,并將類名轉(zhuǎn)換為字符數(shù)組char[] charArray = type.getSimpleName().toCharArray();StringBuilder sb = new StringBuilder(128);// 遍歷字節(jié)數(shù)組for (int i = 0; i < charArray.length; i++) {// 檢測(cè)當(dāng)前字符是否為大寫字母if (Character.isUpperCase(charArray[i])) {if (i != 0) {// 向 sb 中添加點(diǎn)號(hào)sb.append(".");}// 將字符變?yōu)樾?#xff0c;并添加到 sb 中sb.append(Character.toLowerCase(charArray[i]));} else {// 添加字符到 sb 中sb.append(charArray[i]);}}value = new String[]{sb.toString()};}// 省略無關(guān)代碼}// 省略無關(guān)邏輯 }

    ?2.2.3.4 檢測(cè) Invocation 參數(shù)

    此段邏輯是檢測(cè)方法列表中是否存在 Invocation 類型的參數(shù),若存在,則為其生成判空代碼和其他一些代碼。相應(yīng)的邏輯如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 for (Method method : methods) {Class<?> rt = method.getReturnType();Class<?>[] pts = method.getParameterTypes(); // 獲取參數(shù)類型列表Class<?>[] ets = method.getExceptionTypes();Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);StringBuilder code = new StringBuilder(512);if (adaptiveAnnotation == null) {// ${無 Adaptive 注解方法代碼生成}} else {// ${獲取 URL 數(shù)據(jù)}// ${獲取 Adaptive 注解值}boolean hasInvocation = false;// 遍歷參數(shù)類型列表for (int i = 0; i < pts.length; ++i) {// 判斷當(dāng)前參數(shù)名稱是否等于 com.alibaba.dubbo.rpc.Invocationif (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) {// 為 Invocation 類型參數(shù)生成判空代碼String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"invocation == null\");", i);code.append(s);// 生成 getMethodName 方法調(diào)用代碼,格式為:// String methodName = argN.getMethodName();s = String.format("\nString methodName = arg%d.getMethodName();", i);code.append(s);// 設(shè)置 hasInvocation 為 truehasInvocation = true;break;}}}// 省略無關(guān)邏輯 }

    ?2.2.3.5 生成拓展名獲取邏輯

    本段邏輯用于根據(jù) SPI 和 Adaptive 注解值生成“拓展名獲取邏輯”,同時(shí)生成邏輯也受 Invocation 類型參數(shù)影響,綜合因素導(dǎo)致本段邏輯相對(duì)復(fù)雜。本段邏輯可以會(huì)生成但不限于下面的代碼:

    1 String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());

    1 String extName = url.getMethodParameter(methodName, "loadbalance", "random");

    亦或是

    1 String extName = url.getParameter("client", url.getParameter("transporter", "netty"));

    本段邏輯復(fù)雜指出在于條件分支比較多,大家在閱讀源碼時(shí)需要知道每個(gè)條件分支的意義是什么,否則不太容易看懂相關(guān)代碼。好了,其他的就不多說了,開始分析本段邏輯。

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 for (Method method : methods) {Class<?> rt = method.getReturnType();Class<?>[] pts = method.getParameterTypes();Class<?>[] ets = method.getExceptionTypes();Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);StringBuilder code = new StringBuilder(512);if (adaptiveAnnotation == null) {// $無 Adaptive 注解方法代碼生成}} else {// ${獲取 URL 數(shù)據(jù)}// ${獲取 Adaptive 注解值}// ${檢測(cè) Invocation 參數(shù)}// 設(shè)置默認(rèn)拓展名,cachedDefaultName = SPI 注解值,比如 Protocol 接口上標(biāo)注的 // SPI 注解值為 dubbo。默認(rèn)情況下,SPI 注解值為空串,此時(shí) cachedDefaultName = nullString defaultExtName = cachedDefaultName;String getNameCode = null;// 遍歷 value,這里的 value 是 Adaptive 的注解值,2.2.3.3 節(jié)分析過 value 變量的獲取過程。// 此處循環(huán)目的是生成從 URL 中獲取拓展名的代碼,生成的代碼會(huì)賦值給 getNameCode 變量。注意這// 個(gè)循環(huán)的遍歷順序是由后向前遍歷的。for (int i = value.length - 1; i >= 0; --i) {if (i == value.length - 1) { // 當(dāng) i 為最后一個(gè)元素的坐標(biāo)時(shí)if (null != defaultExtName) { // 默認(rèn)拓展名非空// protocol 是 url 的一部分,可通過 getProtocol 方法獲取,其他的則是從// URL 參數(shù)中獲取。所以這里要判斷 value[i] 是否為 protocolif (!"protocol".equals(value[i]))// hasInvocation 用于標(biāo)識(shí)方法參數(shù)列表中是否有 Invocation 類型參數(shù)if (hasInvocation)// 生成的代碼功能等價(jià)于下面的代碼:// url.getMethodParameter(methodName, value[i], defaultExtName)// 以 LoadBalance 接口的 select 方法為例,最終生成的代碼如下:// url.getMethodParameter(methodName, "loadbalance", "random")getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);else// 生成的代碼功能等價(jià)于下面的代碼:// url.getParameter(value[i], defaultExtName)getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);else// 生成的代碼功能等價(jià)于下面的代碼:// ( url.getProtocol() == null ? defaultExtName : url.getProtocol() )getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);} else { // 默認(rèn)拓展名為空if (!"protocol".equals(value[i]))if (hasInvocation)// 生成代碼格式同上getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);else// 生成的代碼功能等價(jià)于下面的代碼:// url.getParameter(value[i])getNameCode = String.format("url.getParameter(\"%s\")", value[i]);else// 生成從 url 中獲取協(xié)議的代碼,比如 "dubbo"getNameCode = "url.getProtocol()";}} else {if (!"protocol".equals(value[i]))if (hasInvocation)// 生成代碼格式同上getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);else// 生成的代碼功能等價(jià)于下面的代碼:// url.getParameter(value[i], getNameCode)// 以 Transporter 接口的 connect 方法為例,最終生成的代碼如下:// url.getParameter("client", url.getParameter("transporter", "netty"))getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);else// 生成的代碼功能等價(jià)于下面的代碼:// url.getProtocol() == null ? getNameCode : url.getProtocol()// 以 Protocol 接口的 connect 方法為例,最終生成的代碼如下:// url.getProtocol() == null ? "dubbo" : url.getProtocol()getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);}}// 生成 extName 賦值代碼code.append("\nString extName = ").append(getNameCode).append(";");// 生成 extName 判空代碼String s = String.format("\nif(extName == null) " +"throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");",type.getName(), Arrays.toString(value));code.append(s);}// 省略無關(guān)邏輯 }

    上面代碼已經(jīng)進(jìn)行了大量的注釋,不過看起來任然不是很好理解。既然如此,那么建議大家寫點(diǎn)測(cè)試代碼,對(duì) Protocol、LoadBalance 以及 Transporter 等接口的自適應(yīng)拓展類代碼生成過程進(jìn)行調(diào)試。這里我以 Transporter 接口的自適應(yīng)拓展類代碼生成過程進(jìn)行分析。首先看一下 Transporter 接口的定義,如下:

    1 2 3 4 5 6 7 8 9 10 @SPI("netty") public interface Transporter {// @Adaptive({server, transporter})@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY}) Server bind(URL url, ChannelHandler handler) throws RemotingException;// @Adaptive({client, transporter})@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})Client connect(URL url, ChannelHandler handler) throws RemotingException; }

    下面對(duì) connect 方法代理邏輯生成的過程進(jìn)行分析,此時(shí)生成代理邏輯所用到的變量和值如下:

    1 2 3 4 String defaultExtName = "netty"; boolean hasInvocation = false; String getNameCode = null; String[] value = ["client", "transporter"];

    下面對(duì) value 數(shù)組進(jìn)行遍歷,此時(shí) i = 1, value[i] = “transporter”,生成的代碼如下:

    1 getNameCode = url.getParameter("transporter", "netty");

    接下來,for 循環(huán)繼續(xù)執(zhí)行,此時(shí) i = 0, value[i] = “client”,生成的代碼如下:

    1 getNameCode = url.getParameter("client", url.getParameter("transporter", "netty"));

    for 循環(huán)結(jié)束運(yùn)行,現(xiàn)在生成 extName 變量及判空代碼,如下:

    1 2 3 4 5 6 String extName = url.getParameter("client", url.getParameter("transporter", "netty")); if (extName == null) {throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString()+ ") use keys([client, transporter])"); }

    到此,connect 方法的拓展名獲取代碼就生成好了。如果大家不是很明白,建議自己調(diào)試走一遍。好了,本節(jié)先到這里。

    ?2.2.3.6 生成拓展加載與目標(biāo)方法調(diào)用邏輯

    上一節(jié)的邏輯生成拓展名 extName 獲取邏輯,接下來要做的是根據(jù)拓展名加載拓展實(shí)例,并調(diào)用拓展實(shí)例的目標(biāo)方法。相關(guān)邏輯如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 for (Method method : methods) {Class<?> rt = method.getReturnType();Class<?>[] pts = method.getParameterTypes();Class<?>[] ets = method.getExceptionTypes();Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);StringBuilder code = new StringBuilder(512);if (adaptiveAnnotation == null) {// $無 Adaptive 注解方法代碼生成}} else {// ${獲取 URL 數(shù)據(jù)}// ${獲取 Adaptive 注解值}// ${檢測(cè) Invocation 參數(shù)}// ${生成拓展名獲取邏輯}// 生成拓展獲取代碼,格式如下:// type全限定名 extension = (type全限定名)ExtensionLoader全限定名// .getExtensionLoader(type全限定名.class).getExtension(extName);// Tips: 格式化字符串中的 %<s 表示使用前一個(gè)轉(zhuǎn)換符所描述的參數(shù),即 type 全限定名s = String.format("\n%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);",type.getName(), ExtensionLoader.class.getSimpleName(), type.getName());code.append(s);// 如果方法有返回值類型非 void,則生成 return 語(yǔ)句。if (!rt.equals(void.class)) {code.append("\nreturn ");}// 生成目標(biāo)方法調(diào)用邏輯,格式為:// extension.方法名(arg0, arg2, ..., argN);s = String.format("extension.%s(", method.getName());code.append(s);for (int i = 0; i < pts.length; i++) {if (i != 0)code.append(", ");code.append("arg").append(i);}code.append(");"); }// 省略無關(guān)邏輯 }

    以 Protocol 接口舉例說明,上面代碼生成的內(nèi)容如下:

    1 2 3 com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName); return extension.refer(arg0, arg1);

    ?2.2.3.7 生成完整的方法

    本節(jié)進(jìn)行代碼生成的收尾工作,主要用于生成方法定義的代碼。相關(guān)邏輯如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 for (Method method : methods) {Class<?> rt = method.getReturnType();Class<?>[] pts = method.getParameterTypes();Class<?>[] ets = method.getExceptionTypes();Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);StringBuilder code = new StringBuilder(512);if (adaptiveAnnotation == null) {// $無 Adaptive 注解方法代碼生成}} else {// ${獲取 URL 數(shù)據(jù)}// ${獲取 Adaptive 注解值}// ${檢測(cè) Invocation 參數(shù)}// ${生成拓展名獲取邏輯}// ${生成拓展加載與目標(biāo)方法調(diào)用邏輯}} }// public + 返回值全限定名 + 方法名 + ( codeBuilder.append("\npublic ").append(rt.getCanonicalName()).append(" ").append(method.getName()).append("(");// 添加參數(shù)列表代碼 for (int i = 0; i < pts.length; i++) {if (i > 0) {codeBuilder.append(", ");}codeBuilder.append(pts[i].getCanonicalName());codeBuilder.append(" ");codeBuilder.append("arg").append(i); } codeBuilder.append(")");// 添加異常拋出代碼 if (ets.length > 0) {codeBuilder.append(" throws ");for (int i = 0; i < ets.length; i++) {if (i > 0) {codeBuilder.append(", ");}codeBuilder.append(ets[i].getCanonicalName());} } codeBuilder.append(" {"); codeBuilder.append(code.toString()); codeBuilder.append("\n}");

    以 Protocol 的 refer 方法為例,上面代碼生成的內(nèi)容如下:

    1 2 3 public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) {// 方法體 }

    ?3.基于動(dòng)態(tài)代理實(shí)現(xiàn)知識(shí)與拓展

    我在第一章介紹自適應(yīng)拓展原理時(shí)說過,Dubbo 通過生成和編譯代碼實(shí)現(xiàn)自適應(yīng)拓展的方式有點(diǎn)復(fù)雜,不利于維護(hù)。另外,這樣做對(duì)源碼學(xué)習(xí)讀者來說,也不是很友好。我敢肯定,有同學(xué)會(huì)像我一樣,在開始調(diào)試 Dubbo 源碼時(shí),不知道如何調(diào)試各種自適應(yīng)拓展類,比如 Protocol$Adaptive。如果你也有類似的困惑,這里教大家一個(gè)方法。如下:

  • 在 createAdaptiveExtensionClass 方法的第一行打個(gè)斷點(diǎn)
  • 啟動(dòng)測(cè)試代碼,代碼運(yùn)行到端點(diǎn)處,單步越過斷點(diǎn),此時(shí)可以得到生成的代碼。
  • 拷貝出剛剛獲取到的代碼,到指定的包下創(chuàng)建同名類,并將代碼拷過去,格式化一下即可
  • 以 Protocol 接口為例,當(dāng)代碼越過斷點(diǎn)后,調(diào)試信息如下:

    從調(diào)試信息中可知,ProtocolAdaptive 所在包為 com.alibaba.dubbo.rpc。因此接下來到 com.alibaba.dubbo.rpc 包下創(chuàng)建 ProtocolAdaptive 類,并把 code 變量值拷貝到剛創(chuàng)建的文件中。當(dāng)我們?cè)俅芜M(jìn)行調(diào)試時(shí),就能進(jìn)入內(nèi)部了。比如:

    既然 Dubbo 實(shí)現(xiàn)的 Adaptive 機(jī)制不利于調(diào)試,那么我們可以對(duì)其進(jìn)行改造。改造后的代碼如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class AdaptiveInvokeHandler implements InvocationHandler {private String defaultExtName;public AdaptiveInvokeHandler(String defaultExtName) {this.defaultExtName = defaultExtName;}public Object getProxy(Class clazz) {if (!clazz.isInterface()) {throw new IllegalStateException("Only create the proxy for interface.");}return Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Class<?> type = method.getDeclaringClass();if (type.equals(Object.class)) {throw new UnsupportedOperationException("Cannot invoke the method of Object");}Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);if (adaptiveAnnotation == null) {throw new UnsupportedOperationException("method " + method.toString() + " of interface " + type.getName() + " is not adaptive method!");}// 獲取 URL 數(shù)據(jù)URL url = getUrlData(method, args);// 獲取 Adaptive 注解值String[] value = getAdaptiveAnnotationValue(method);// 獲取 Invocation 參數(shù)Object invocation = getInvocationArgument(method, args);// 獲取拓展名String extName = getExtensionName(url, value, invocation);if (StringUtils.isEmpty(extName)) {throw new IllegalStateException("Fail to get extension(" + type.getName() + ") name from url(" + url.toString()+ ") use keys(" + Arrays.toString(value) +")");}// 獲取拓展實(shí)例Object extension = ExtensionLoader.getExtensionLoader(type).getExtension(extName);Class<?> extType = extension.getClass();Method targetMethod = extType.getMethod(method.getName(), method.getParameterTypes());// 通過反射調(diào)用目標(biāo)方法return targetMethod.invoke(extension, args);} }

    這樣看起來是不是簡(jiǎn)單了一些,不過這并不是全部的代碼。我將 URL 數(shù)據(jù)以及 Adaptive 注解值的獲取邏輯封裝在了私有方法中,相應(yīng)的代碼如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 private URL getUrlData(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {URL url = null;Class<?>[] pts = method.getParameterTypes();for (int i = 0; i < pts.length; i++) {if (pts[i].equals(URL.class)) {url = (URL) args[i];if (url == null) {throw new IllegalArgumentException("url == null");}break;}}if (url == null) {int urlTypeIndex = -1;Method getter = null;LBL_PTS:for (int i = 0; i < pts.length; ++i) {Method[] ms = pts[i].getMethods();for (Method m : ms) {String name = m.getName();if ((name.startsWith("get") || name.length() > 3)&& Modifier.isPublic(m.getModifiers())&& !Modifier.isStatic(m.getModifiers())&& m.getParameterTypes().length == 0&& m.getReturnType() == URL.class) {urlTypeIndex = i;getter = m;break LBL_PTS;}}}if (urlTypeIndex == -1) {throw new IllegalArgumentException("Cannot find URL argument.");}if (args[urlTypeIndex] == null) {throw new IllegalArgumentException(pts[urlTypeIndex].getName() + " argument == null");}url = (URL) getter.invoke(args[urlTypeIndex]);if (url == null) {throw new IllegalArgumentException(pts[urlTypeIndex].getName() + " argument " + getter.getName() + "() == null");}}return url; }private String[] getAdaptiveAnnotationValue(Method method) {Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);Class type = method.getDeclaringClass();if (adaptiveAnnotation == null) {throw new IllegalArgumentException("method " + method.toString() + " of interface " + type.getName() + " is not adaptive method!");}String[] value = adaptiveAnnotation.value();if (value.length == 0) {char[] charArray = type.getSimpleName().toCharArray();StringBuilder sb = new StringBuilder(128);for (int i = 0; i < charArray.length; i++) {if (Character.isUpperCase(charArray[i])) {if (i != 0) {sb.append(".");}sb.append(Character.toLowerCase(charArray[i]));} else {sb.append(charArray[i]);}}value = new String[]{sb.toString()};}return value; }private Object getInvocationArgument(Method method, Object[] args) {Class<?>[] pts = method.getParameterTypes();for (int i = 0; i < pts.length; ++i) {if (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) {Object invocation = args[i];if (invocation == null) {throw new IllegalArgumentException("invocation == null");}return invocation;}}return null; }private String getExtensionName(URL url, String[] value, Invocation invocation) {String methodName = null;boolean hasInvocation = invocation != null;if (hasInvocation) {Class<?> clazz = invocation.getClass();Method method = clazz.getMethod("getMethodName");methodName = (String) method.invoke(invocation);}String extName = null;for (int i = 0; i < value.length; i++) {if (!"protocol".equals(value[i])) {if (hasInvocation) {extName = url.getMethodParameter(methodName, value[i], defaultExtName);} else {extName = url.getParameter(value[i]);}} else {extName = url.getProtocol();}if (StringUtils.isNotEmpty(extName)) {break;}if (i == value.length -1 && StringUtils.isEmpty(extName)) {extName = defaultExtName;}}return extName; }

    現(xiàn)在我們將 AdaptiveInvokeHandler 放置到 ExtensionLoader 所在包下,并對(duì) ExtensionLoader 的 createAdaptiveExtension 方法代碼進(jìn)行改造。如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private T createAdaptiveExtension() {try {getExtensionClasses();T extension = null;if (cachedAdaptiveClass != null) {extension = (T) cachedAdaptiveClass.newInstance();}if (extension == null) {extension = (T) new AdaptiveInvokeHandler(cachedDefaultName).getProxy(type);}return injectExtension(extension);} catch (Exception e) {throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);} }

    以上就是改造后的代碼,需要特別說明的是,上面的代碼僅供演示使用,代碼邏輯并不是十分嚴(yán)謹(jǐn)。如果你有更好的寫法,歡迎分享。

    ?4.總結(jié)

    到此,關(guān)于自適應(yīng)拓展的原理,實(shí)現(xiàn)以及改造過程就分析完了。總的來說自適應(yīng)拓展整個(gè)邏輯還是很復(fù)雜的,并不是很容易弄懂。因此,大家在閱讀該部分源碼時(shí),耐心一些,同時(shí)多進(jìn)行調(diào)試。亦或是通過生成好的代碼思考生成邏輯。當(dāng)然,大家也可以將代碼生成邏輯看成一個(gè)黑盒,不懂細(xì)節(jié)也沒關(guān)系,只要知道自適應(yīng)拓展原理即可。

    好了,本篇文章先到這里,感謝大家的閱讀。

    • 本文鏈接:?https://www.tianxiaobo.com/2018/10/13/Dubbo-源碼分析-自適應(yīng)拓展原理/

    http://www.tianxiaobo.com/2018/10/13/Dubbo-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E8%87%AA%E9%80%82%E5%BA%94%E6%8B%93%E5%B1%95%E5%8E%9F%E7%90%86/?

    總結(jié)

    以上是生活随笔為你收集整理的Dubbo 源码分析 - 自适应拓展原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。