順著dubbo入口擼ExtensionLoader源碼

參考

屬于在這篇Dubbo擴展點加載基礎(chǔ)上的展開學(xué)習(xí)。但原文有點小問題(Container啟動那里),所以本文直接按自己的理解來組織。不加說明的引用都來自該文。

問題

Dubbo的原理、核心的概念很多文章都有詳細的介紹(什么SPI、擴展點、Adaptive、ExtensionLoader等等),但是我的問題是它們是如何運行、如何起作用、整體的流程是什么?

1. Dubbo入口

執(zhí)行Dubbo,實際上是執(zhí)行了com.alibaba.dubbo.container.Main.main

Main這個類中有一些靜態(tài)成員變量,最重要的是這個:

private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);

從這里開始使用ExtensionLoader。

2. 初始化Main的靜態(tài)成員,獲得Container類型的ExtensionLoader

ExtensionLoader類中有個靜態(tài)的final變量EXTENSION_LOADERS緩存了各種類型的ExtensionLoader,如果已有緩存,就直接獲取,如果沒有就調(diào)用new方法新建。

啟動時getExtensionLoader(Container.class)獲取Container類型的ExtensionLoader肯定不存在,去構(gòu)造:

private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

ExtensionLoader的構(gòu)造方法中可以看到,ExtensionLoader有兩個屬性,一個是type,標(biāo)識了這個ExtensionLoader所屬的類型,另一個是ObjectFactory,類型是ExtensionFactory。

那么,ExtensionLoader<Container>this.type=Container.class,
而根據(jù)構(gòu)造函數(shù),欲要得到Container.ClassObjectFactory,須先得到ExtensionFactory類型的ExtensionLoader

2.1 獲取ExtensionFactory類型的ExtensionLoader

同樣沒有,去構(gòu)造,根據(jù)構(gòu)造函數(shù)ExtensionLoader<ExtensionFactory>this.type=ExtensionFactory.Class,ObjectFactory=null
繼續(xù)。

2.2 獲取ExtensionFactory的擴展實現(xiàn):getAdaptiveExtension()

Dubbo的微內(nèi)核做得非常的徹底,ExtensionFactory也是一個擴展點,也需要通過ExtensionLoader<ExtensionFactory>加載

因為(Container的)ObjectFactoryExtensionFactory類型,而ExtensionFactory是一個擴展點,那么就要用ExtensionFactory.ClassExtensionLoader通過getAdaptiveExtension()獲取到ExtensionFactory.Class的Adaptive實現(xiàn),再給到(Container的)ObjectFactory

getAdaptiveExtension()中,首先也是試圖從ExtensionLoader<ExtensionFactory>的一個緩存cachedAdaptiveInstance中取,取不到就調(diào)用createAdaptiveExtension()創(chuàng)建。

2.2.1 創(chuàng)建ExtensionFactory擴展: createAdaptiveExtension()

邏輯就一句:

injectExtension((T) getAdaptiveExtensionClass().newInstance());

先看 getAdaptiveExtensionClass()

2.2.1.1 獲取ExtensionFactory擴展類:getAdaptiveExtensionClass()

private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

這里整體的邏輯是,先getExtensionClasses(),看看有沒有已經(jīng)存在的擴展實現(xiàn)類,有的話取到這些類,把標(biāo)記了@Adaptive的緩存到cachedAdaptiveClass(比如說ExtensionFactory就是有的),把沒有標(biāo)記@Adaptive的擴展實現(xiàn)類緩存到cachedClasses,返回。

如果沒有的話,就調(diào)用createAdaptiveExtensionClass()現(xiàn)生成code并compile一個Adaptive類出來,同樣緩存到cachedAdaptiveClass,返回。

Adaptive注解可以標(biāo)記在類上,也可以標(biāo)記在方法上。
如果Adaptive類選擇擴展點實現(xiàn)的依據(jù)是根據(jù)上下文信息,即URL中的信息選擇不同的實現(xiàn),那么可以把Adaptive注解標(biāo)記在服務(wù)的方法上,ExtensionLoader可以自動生成這種情況的Adaptive類。
如果是自己實現(xiàn)Adaptive類,那么需要在類上標(biāo)記Adaptive注解,ExtensionLoader在加載擴展點時就能發(fā)現(xiàn)并創(chuàng)建Adaptive實現(xiàn)。

(來自Dubbo源碼解析-2:Adaptive類,同樣很厲害的文章嗯,本文對其也多有引用不一一列舉了)

下面詳細分析一下這兩步。

2.2.1.1.1 獲取已存在的擴展類:getExtensionClasses()

首先檢查ExtensionLoader<ExtensionFactory>的緩存cachedClasses,有就get沒有就調(diào)用loadExtensionClasses()創(chuàng)建。

  • loadExtensionClasses()
    首先去拿SPI注解,如果注解不為null,獲取注解的value:
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
...
String value = defaultAnnotation.value();

ExtensionFactory定義如下:

@SPI
public interface ExtensionFactory {

    /**
     * Get extension.
     * 
     * @param type object type.
     * @param name object name.
     * @return object instance.
     */
    <T> T getExtension(Class<T> type, String name);

}

SPI注解類定義如下:

public @interface SPI {

    /**
     * 缺省擴展點名。
     */
    String value() default "";

}

可以看到ExtensionFactory沒有定義缺省擴展點名(后面會看到Container類有缺省定義)。

有缺省擴展點名就緩存到ExtensionLoader<ExtensionFactory>的另一個緩存cachedDefaultName,沒有就繼續(xù)。

繼續(xù),調(diào)用loadFile裝載擴展實現(xiàn)類。

Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
  • loadFile(Map<String, Class<?>> extensionClasses, String dir)

loadFile首先在指定目錄下找名字是type的配置文件,然后讀出來:

String fileName = dir + type.getName();

對 于 ExtensionFactory來 說 , 會讀到:

//META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
//META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory

隨后:

  1. loadfile讀入第一行數(shù)據(jù),初始化:name=adaptive, line = com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory

  2. 使用Class<?> clazz = Class.forName(line, true, classLoader)加載類。

  3. !type.isAssignableFrom(clazz):驗證加載的類是否是當(dāng)前type的一個實現(xiàn)。

  4. clazz.isAnnotationPresent(Adaptive.class):檢查如果設(shè)置了@Adaptive,則把clazz保存在cachedAdaptiveClass
    對于ExtensionFactory來說,AdaptiveExtensionFactory設(shè)置了@Adaptive注解:

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
...
}
  1. 對于沒有設(shè)置@Adaptive的類,則存入loadExtensionClasses()傳到loadFile()中的參數(shù)extensionClasses,返回后在getExtensionClasses() 中賦給cachedClasses緩存。
classes = loadExtensionClasses();
cachedClasses.set(classes);

對于ExtensionFactory來說,SpiExtensionFactorySpringExtensionFactory都沒有設(shè)置@Adaptive注解(同一個類只能有一個Adaptive實現(xiàn)),所以都被存入了ExtensionLoader<ExtensionFactory>cachedClasses。

2.2.1.1.2 動態(tài)生成沒有自己實現(xiàn)的Adaptive類:createAdaptiveExtensionClass()

執(zhí)行完getExtensionClasses(),回到了2.2.1.1的getAdaptiveExtensionClass(),如果此時cachedAdaptiveClass仍為null,說明沒有找到標(biāo)記了@Adaptive的類,需要根據(jù)上下文動態(tài)生成。

private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

動態(tài)生成的過程簡單來說就是找到標(biāo)記了@Adaptive的方法,根據(jù)type通過StringBuilder拼接一個類,加載Compiler編譯。

前面提到的參考文章(Dubbo源碼解析-2:Adaptive類)里舉了例子,可以看到生成了什么樣子的代碼。這里不詳細說了。

2.2.1.2 為擴展注入依賴的其他擴展實現(xiàn):injectExtension(T instance)

好的終于回來了,2.2.1.1節(jié)完成了getAdaptiveExtensionClass(),并返回了cachedAdaptiveClass。 為這個cachedAdaptiveClassnew了一個Instance以后,開始injectExtension注入。

擴展點注入的代碼如下,首先查找以set開頭、帶一個參數(shù)的public方法,如果set方法的名稱大于3,則根據(jù)名稱獲取參數(shù)擴展點的名稱,然后獲取擴展點實現(xiàn)的實例,這可能又是一個次配置的解析和實例化過程。最后調(diào)用set方法將實例設(shè)置進去。

private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                for (Method method : instance.getClass().getMethods()) {
                    if (method.getName().startsWith("set")&&...) {
                        Class<?> pt = method.getParameterTypes()[0];
                        try {
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        } catch (Exception e) {
                            ...
                        }
                    }
                }
            }
        } catch (Exception e) {
           ...
        }
        return instance;
    }

對于ExtensionFactory來說,首先判斷,if (objectFactory != null),則可以直接返回cachedAdaptiveClass所new的instance了。

2.2.2 ExtensionLoader<Container>構(gòu)造完畢

整個2.2.1過程結(jié)束,拿到了ExtensionFactory.class的擴展實現(xiàn),也就是所返回的cachedAdaptiveClass,即AdaptiveExtensionFactory

private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

回到構(gòu)造函數(shù),ExtensionLoader<Container>objectFactory =AdaptiveExtensionFactory。

ExtensionLoader<Container>構(gòu)造完畢。

3. 在main()中調(diào)用loader.getExtension()加載Container的擴展實現(xiàn)

Main函數(shù)的整體邏輯是,如果沒有傳入?yún)?shù),就裝載Container的SPI注解指定的默認Container,如果傳入的參數(shù)(main函數(shù)參數(shù)、JVM啟動參數(shù)、classpath下的dubbo.properties配置等)指定了Container,就裝載通過參數(shù)指定的Container。

public class Main {
    public static final String CONTAINER_KEY = "dubbo.container";
    private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
    ...    
    public static void main(String[] args) {
        try {
            if (args == null || args.length == 0) {
                String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
                args = Constants.COMMA_SPLIT_PATTERN.split(config);
            }
            
            final List<Container> containers = new ArrayList<Container>();
            for (int i = 0; i < args.length; i ++) {
                containers.add(loader.getExtension(args[i]));
            }
            ...
            for (Container container : containers) {
                container.start();
               ...
            }
            ...
    }
    
}

首先看Container類的定義:

@SPI("spring")
public interface Container {
    
    /**
     * start.
     */
    void start();
    
    /**
     * stop.
     */
    void stop();

}

它將SPI的value值設(shè)置Spring,也就是指定默認的擴展實現(xiàn)名稱是spring(默認情況只加載一個spring容器)。

再看關(guān)于Container的配置(以spring為例,其他jetty、log4j等都類似):

\\META-INF/dubbo/internal/com.alibaba.dubbo.container.Container
spring=com.alibaba.dubbo.container.spring.SpringContainer

而SpringContainer類沒有加注解什么的,所以這里是通過SPI注解的默認值控制的,和ExtensionFactory的機制不同。

下面詳細分析main函數(shù)加載Container擴展點過程。

3.1 如果沒有傳參,獲得默認擴展名:loader.getDefaultExtensionName()

    public String getDefaultExtensionName() {
        getExtensionClasses();
        return cachedDefaultName;
    }

這里調(diào)用了getExtensionClasses(),與2.2.1.1.1節(jié)介紹的過程相同。

不同的是,對于ExtensionLoader<Container>,通過SPI注解定義了缺省擴展點名spring,因此會將spring緩存到cachedDefaultName。

而由于Container的擴展實現(xiàn)類都沒有設(shè)置@Adaptive,則這些實現(xiàn)類都被緩存在cachedClasses中,不會被緩存在cachedAdaptiveClass

3.2 獲得Container:loader.getExtension()

  • getExtension()
    先去緩存cachedInstances中找,如果沒有,則調(diào)用createExtension創(chuàng)建。
public T getExtension(String name) {
        ...
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
  • createExtension()
    通過getExtensionClasses()獲得擴展實現(xiàn)類,調(diào)用injectExtension注入依賴的其他擴展和包裝類。返回獲取了包裝后的擴展instance。
private T createExtension(String name) {
        Class<?> clazz = getExtensionClasses().get(name);
        ...
        try {
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
           ...
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            ...
        }
    }
  • getExtensionClasses()

其實是從獲取之前l(fā)oad到cachedClassed中的所有擴展實現(xiàn)類。沒有的話就調(diào)用loadExtensionClasses()(見2.2.1.1.1節(jié))獲取。

private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            ...
            classes = loadExtensionClasses();
            ...
        }
        return classes;
    }

3.3 獲取Container擴展后,啟動

擴展加載結(jié)束,啟動Container~

for (Container container : containers) {
       container.start();
       ...
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容