探究Dubbo的拓展機制: 下


承接上篇, 本篇博文的主題就是認認真真捋一捋, 看一下 Dubbo是如何實現他的IOC / AOP / 以及Dubbo SPI這個拓展點的

總覽:

本篇的話總體上分成兩部分進行展開

  • 第一點就是 Dubbo在啟動過程中加載原生的配置文件中提供的被@SPI標記的實現類:

概要1

  • 第二就是Dubbo加載程序員后續添加進去的被@SPI標注的接口和實現類, 進而探究 Dubbo的IOC / AOP / 以及Dubbo SPI這個拓展點機制

概要

環境的初始化

入口程序

如下代碼是追蹤的起點:

我也是看了好多遍才勉強將這個過程整理明白一些, 但是根據以往的經驗來說, 過一倆月之后我可能就會淡忘這個流程... 為了讓自己一段時間后快速的回憶起來這個流程, 所以我要對自己說下面一段話

Dubbo的拓展點編碼實現中, 會反反復復的出現下面這段代碼

**ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(XXX.class); extensionLoader.getExtension("XXX"); **

先說這段代碼在干什么? 其實上它就是在為 Dubbo原生的SPI接口, 或者是用戶提供的SPI接口 結合SPI的配置文件中的配置, 找到這些SPI接口的實現類, 並且這個過程中穿插Dubbo的IOC已經AOP機制

**不得不服氣, 這一段代碼的實現, 因為這段代碼設計不僅僅能加載Dubbo提供的原生的SPI接口, 也能加載使用 用戶自定義的SPI , 詳細的過程在下文中展開, 妙!!! **

啟動類

明星類 ExtensionLoader.java

應該得, 隆重的介紹一下這個明星類 ExtensionLoader.java

從名字上看, ExtensionLoader , 見名知意, 拓展點的加載器, 那什么是Dubbo的拓展點呢? 拓展點就是Dubbo允許用戶參與到Dubbo環境的初始化這個過程中來, 允許用戶定制Dubbo行為, 諸如 Dubbo的 SPI / IOC / AOP (上一篇博文主要的學習內容就是Dubbo的拓展點的使用)

見名知意: ExtensionLoader 拓展點的加載器, 就是使用這個封裝類, 我們可以加載Dubbo提供的拓展點, 說白了, 其實加載為SPI接口找到實現類, 以及完成這些實現類之間的 AOP增強 + IOC 依賴注入的過程

此外這個類很有必要看, 為啥呢? 第一點就是說它的設計很巧妙, 代碼的抽象和復用能力都很好, 第二點就是說, 我們可以一睹大神們的風采, 如果 實現自己的SPI , 如何實現自己的IOC AOP

extensionLoader

入口方法

下面就是入口程序中的第一個方法, getExtensionLoader(Class<T> type) 很簡單, 就是根據類型找到對應的 ExtensionLoader, 待會Dubbo就會為我添加進去SPI接口生成這樣的 ExtensionLoader : org.apache.dubbo.common.extension.ExtensionLoader[com.changwu.ioc.api.PersonInterface]

當然Dubbo也有自己原生的ExtensionLoader

從我的入口程序來看, 很顯然, 我傳遞進來的 type = PersonInterface , 方法執行的邏輯如下

  • 對type參數進行校驗
  • 檢查緩存中是否存在 PersonInterface的 ExtensionLoader
    • 如果有的話, 返回這個現存的ExtensionLoader
    • 如果不存在就創建一個新的
 public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }

        //todo 這里體現了緩存機制, EXTENSION_LOADERS 其實就是 CurrentHashMap
        //todo EXTENSION_LOADERS  是 CurrentHashMap , 每一種interfaceType 都對應一個 ExtensionLoader , 但是這些 ExtensionLoader全部被維護在 這個EXTENSION_LOADER中
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }

ExtensionLoader蹊蹺的構造方法

那我們是第一次進來, 肯定是沒有的, 因此我們看他是如何進行new ExtensionLoader<T>(type)的, 所以跟進看一下它的構造方法

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        // 對於一個接口,比如PersonInterface接口,有兩種實現類,一種就是我們自定義的實現類,比如Student,還有一種就是代理類,對於代理類,可以由我們自己實現,也可以讓Dubbo幫我們實現,而代理類主要就是依賴注入時使用
        // todo ExtensionFactory 是dubbo的拓展機制工廠, 它里面封裝了 Dubbo的SPI拓展機制和Spring的拓展機制
        // todo ExtensionLoader.getExtensionLoader(ExtensionFactory.class) ===>  獲取  自適應的extension
        //  ||          ||          ||             ||               ||          ||
        // todo ExtensionLoader.getExtensionLoader(PersonInterface.class)  ===>  昌武, 你獲取的是:  extension("human")
        // todo 你看這是不是挺清晰的
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

在上面的構造方法中, 就有蹊蹺了, 邏輯如下

  • 將type = PersonInterface 保存起來
  • 獲取 ExtensionFactory.class 類型的 ExtensionLoader
  • 獲取 ExtensionFactory.class 類型的 ExtensionLoader的自適應的 Extension

**我所說的有蹊蹺的地方: ** 我們本來不是前來創建PersionInterface 的ExtensionLoader嗎? 怎么先創建 ExtensionFactory的 ExtensionLoader呢?

(因為在創建ExtensionFactory的 ExtensionLoader的過程中會去加載Dubbo提供的其他的諸如SpiExtensionFactory這一類的實現, 這些默認的實現的作用就是輔助Dubbo再去解析用戶提供的SPI實現體系)

下面看看這個 ExtensionFactory.class類

沒錯! 它被添加上了@SPI的注解, 說明和 我們的PersonInterface一樣, 是DubboSPI

ExtensionFactory

那好吧, 既然Dubbo想先完成它的實例化, 就往下看, 我在博文開頭就不停的說, Dubbo設計的很好, 這里不就遞歸調用getExtensionLoader(type= ExtensionFactory.class)了嗎? 不出意外的話, 再一次的 進去構造方法, 然后在這個三元判斷表達式中發現了 type == ExtensionFactory.class ? null : ExtensionLoader.getE... 前半部分是滿足條件的, 然后設置objectFactory=null, **完成 ExtensionFactory的構造, 然后執行getAdaptiveExtension() **

獲取自適應的Extension

這個 getAdaptiveExtension() 同樣需要好好的看看, 見名知意, 返回一個自適應的 Extension, 說白了就是返回Dubbo通過字符拼接出來的Extension類

下面看看這個 getAdaptiveExtension() 源碼如下:

    @SuppressWarnings("unchecked")
    public T getAdaptiveExtension() {
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                // todo 為了線程安全 , 使用了雙重同步鎖
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                            // todo 跟進去
                            instance = createAdaptiveExtension();
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }

        return (T) instance;
    }

着重跟進 instance = createAdaptiveExtension();方法, 源碼如下: 主要邏輯如下:

  • 獲取出 AdaptiveExtensionClass
    • 啟動過程中, 第一次獲取到的是 Dubbo提供的默認實現類, 叫 AdaptiveExtensionFactory
    • 第二次獲取到的是 Dubbo為用戶提供的SPI接口動態生成的實現類
  • 實例化AdaptiveExtensionClass
  • 對實例化的AdaptiveExtensionClass 進行依賴注入的操作

crateAdaptiveExtension

加載SPI配置文件, 獲取所有的ExtensionClass

ExtensionClass 可以直白的理解成 SPI 接口的實現類, 或者是wrapper類

上面的代碼中想要獲取一個 AdaptiveExtensionClass() 那么問題來了, 從哪里獲取呢? 跟進getAdaptiveExtensionClass()

沒錯就在下面的

    private Class<?> getAdaptiveExtensionClass() {
        // todo 加載配置文件
        getExtensionClasses();
        // todo 在前一步加載配置文件時, 加載到了 AdaptiveExtensionFactory, 這里返回的就是 CachedAdaptiveClass
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        // 如果沒有手動實現接口的代理類,那么Dubbo就會自動給你實現一個
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

往下跟進 getExtensionClasses();

下面的函數中維護着一個 cachedClasses 它是一個Map , key=String value= Class ; 說白了, 存放的就是從SPI配置文件中讀取配置信息

  // 實際上就是將配置中的 key=value 讀取裝在進map中
    private Map<String, Class<?>> getExtensionClasses() {
        // todo  cachedClasses是 ExtensionLoader的屬性: Holder<Map<String, Class<?>>> cachedClasses
        // todo  用於存儲提前約定好了存儲在 類路徑下的  METE-INF/services  以及dubbo原生提供的擴展點
        // todo  同樣是雙重同步鎖 + volatile  保證線程安全
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    // todo 着重看這個函數
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

進行跟進loadExtensionClasses();

可以看到, Dubbo會按照約定讀取下面幾個配置文件中的配置信息, 下面我注釋上的文件的全路徑所對應的文件中會記錄Dubbo原生的SPI的實現, 我們也能遵循這個規則提供自己的實現類

比如隨便查看一個配置文件

配置文件圖

    // synchronized in getExtensionClasses
    private Map<String, Class<?>> loadExtensionClasses() {
        cacheDefaultExtensionName();

        Map<String, Class<?>> extensionClasses = new HashMap<>();
        // todo 跟進這個 loadDirectory() 方法, 看看
        // todo    META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionFactory
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());

        // todo    META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));

        // todo    META-INF/dubbo/org.apache.dubbo.common.extension.ExtensionFactory
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());

        //todo     META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));

        //todo     META-INF/services/org.apache.dubbo.common.extension.ExtensionFactory
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());

        //todo     META-INF/services/com.alibaba.dubbo.common.extension.ExtensionFactory
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        return extensionClasses;
    }

下面看一下處理的詳細細節信息:

  • 如果從配置文件中讀取到的 SPI的實現類添加了@Adaptive注解, 就先緩存起來

    • Dubbo將 AdaptiveExtensionFactory.java暫時緩存起來了
    • 圖5
  • 沒添加@Adaptive的話, 同樣將其緩存在不同額容器中, 稍后使用

    • Dubbo創建的實例對象是: SpiExtensionFactory,java
    • SPIExtensionFactory
    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        if (!type.isAssignableFrom(clazz)) {
            throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                    type + ", class line: " + clazz.getName() + "), class "
                    + clazz.getName() + " is not subtype of interface.");
        }
        // todo 查看有沒有標注 @Adaptive 注解, 如果標注有這個注解的話, 那么就將他暫時存放起來, 而不是執行下面的邏輯, 構造出對象來
        // todo 昌武, 你看, 你在驗證ioc時, 你提供的PersonInterface很顯然是存在這個@Adaptive注解 ,他會在上面提到getAdaptiveClasss() 后 然后newInstance()創建實例

        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz);
        } else if (isWrapperClass(clazz)) {
            cacheWrapperClass(clazz);
        } else {
            clazz.getConstructor();
            if (StringUtils.isEmpty(name)) {
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }

            String[] names = NAME_SEPARATOR.split(name);
            if (ArrayUtils.isNotEmpty(names)) {
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    cacheName(clazz, n);
                    saveInExtensionClass(extensionClasses, clazz, name);
                }
            }
        }
    }

小結: 到這里基本上就到了Dubbo的底層確實會去讀取配置文件, 根據他們的配置情況, 緩存在不同容器中

好, 到這里上面所說的getExtensionClasses(); 方法就說完了, 回到下面的方法中

crateAdaptiveExtension

得到了AdaptiveExtensionFactory類之后, 接着就通過反射創建的它實例對象, 所以說, 我們要去看他的構造方法, 如下:

  • AdaptiveExtensionFactory繼承了 ExtensionFactory, 因此它需要重寫 getExtension(Class<T> type, String name)
  • 重點看他的構造方法

又看到了這行代碼 ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class); 這行代碼的執行流程其實已經說過了, 這次根據名稱獲取的 Extension是 SpiExtensionFactory, 並將它維護起來

圖1

新的問題就來了, 這個SPIExtensionFactory是誰呢? 有啥用呢 看下面, 說白了, 用它處理添加有Dubbo的SPI注解的接口, 然后嘗試獲取這些接口的 實現

SPIExtensionFactory

構建方法執行完成了, 也就說明 AdaptiveExtension 創建完成了, 剛才所說的 createAdaptiveExtension

injectExtension其實就是回去做IOC / AOP 相關的操作, 現在我們跟蹤的實現類是 AdaptiveExtension 它沒有依賴其他的屬性, 但是我提供的PersonInterface依賴了, 所以說我們暫時先不進如這個方法,稍后再進去查看他的實現

crateAdaptiveExtension

**小結: 下圖是我們的啟動類, 到目前為止, 我們就看完了啟動類的第一行代碼做了什么, 那它主要是做了哪些事情呢? **

  • 實例化 : AdaptiveExtensionFactory
  • 實例化 : SPIExtensionFactory
    • 可用來處理用戶后續添加進來的SPI相關邏輯
  • 實例化 : 用戶提供的Spi接口的 ExtensionLoader

啟動類

Dubbo的IOC細節

下面就繼續看這行extensionLoader.getExtension("human") , 看他的返回值, 很明顯, 就是要返回我們需要的personInterface的實現類, 並在這個過程中穿插這IOC和AOP的邏輯

回顧一下實驗的環境, 重新整理一下思路: 我們想獲取出 key = human的 PersonInterface的實現類, 這實現類長下面這樣:

public class Human implements PersonInterface {

    private PersonInterface personInterface;

    //todo 第一個關注點: 我們的關注點就是說, Human 會幫我們將哪一個實現類當成入參注入進來呢?
    //todo 答案是 URL ,dubbo自己封裝的URL,  統一資源定位符, dubbo 會解析入參位置的 url中封裝的map
    //todo map中的key 與 PersonInteface中的使用   @Adaptive("person") 注解標記的value對應, 那么值就是將要注入的實際類型
    //todo 第二個關注點: dubbo底層很可能是通過反射使用構造方法完成的屬性注入
    public void setPersonInterface(PersonInterface personInterface) {
        this.personInterface = personInterface;
    }

    @Override
    public String getName(URL url) {
        System.out.println("i am Human ");
        return "i am Human + " + personInterface.getName(url);
    }
}

可以很直接的看到, 這個實現類其實是依賴了一個 PersionInterface的屬性,需要將這個屬性注入給他, 於是問題來了, 注入的是誰呢? 下面繼續往下拉看

進入下面的方法, 主要邏輯如下

  • 根據那么取出ExtensionClasses的 Class 對象,
    • 這獲取出來的對象就是我們前面所說的,就是讀取SPI配置文件時獲取出來的對象
  • 調用injectExtension()方法, 完成對象依賴屬性的注入
  • 實現Dubbo的AOP , 完成對象方法切面的增強

ioc和aop的預覽

我們先看下: injectExtension(instance)的實現細節:

主要邏輯如下:

  • 通過反射獲取出對象的所有的方法
    • 如果不是setter方法就返回 (體現出, Dubbo的依賴注入是借助setter方法實現的)
    • 如果添加的@Disable注解, 表示明確指定不會進行注入
    • 嘗試獲取出當前對象所依賴的對象, 也就是下面的objectFactory.getExtension(pt,property)
      • 其中objectFactory就是前面創建出來的SPIExtensionFactory
      • pt=PersonInterface的Class 描述對象
      • property 是從 setPersonInterface()方法中截取出來的: personInterface 字符串

ioc

上圖中的主要目的就是完成依賴注入, 什么依賴注入呢? 就是在 Human.java中 依賴了一個PersonInterface類型的屬性, Dubbo需要幫我填充上 , HumanInterface.java 中鎖依賴的那個具體的實現類是誰呢? 就是在上面函數中的通過 objectFactory.getExtension(Class,name) 動態生成出來的

當我們繼續跟進這個getExtension(), 就會發現下面的現象, 看我在下圖中標出來的綠色部分, 可以發現 , 他獲取出來的 ExtensionLoader全稱如下: 它就是Dubbo我們生成出來的代理 ExtensionLoader

圖3

再進一步, 通過loader 獲取出自適應的拓展類: getAdapativeExtension()通過反編譯看一下生成的Interface是誰, 可以看一下,它的實現, 這就是為什么Dubbo通過URL就能知道該注入誰, 用誰取干活

反編譯

Dubbo的AOP細節 (wrapper)

先說啥是AOP, 就是面向切面編程, 其實說白了就是對現有的對象進行增強

Dubbo是怎么做的呢? 按照Dubbo的約定, 我們的這樣編碼:即 通過繼承+構造方法 實現AOP , 就像下面這樣

wapper2

Dubbo的底層實現: 處理AOP的邏輯在下面

wrapper

Dubbo會從SPI配置文件中找到我們添加就進去的Wrapperlei, 通過構造方法反射出他們的實例,, 重要的是將反射出來的這個實例替換成了原來未被增強的 對象, 就跟java的感覺就像是升級版的靜態代理一樣

最后打一個小廣告: 我是bloger 賜我白日夢, 本科大三在讀, 熱衷java研發, 期望有一份Java相關實習崗位的工作, 可以全職實習半年左右, 最理想城市是北京, 求大佬的內推哇


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM