探究Dubbo的拓展機制: 下

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

總覽:

本篇的話整體上分紅兩部分進行展開程序員

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

概要1

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

概要

環境的初始化

入口程序

以下代碼是追蹤的起點:apache

我也是看了好多遍才勉強將這個過程整理明白一些, 可是根據以往的經驗來講, 過一倆月以後我可能就會淡忘這個流程... 爲了讓本身一段時間後快速的回憶起來這個流程, 因此我要對本身說下面一段話編程

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

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

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

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

啓動類

明星類 ExtensionLoader.java

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

從名字上看, 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相關實習崗位的工做, 能夠全職實習半年左右, 最理想城市是北京, 求大佬的內推哇

相關文章
相關標籤/搜索