成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

dubbo擴(kuò)展點機制

Rindia / 3166人閱讀

摘要:在中配置,以配置為例整個,最先使用的地方從里面讀取這個配置使用接口的中獲取具體的實現(xiàn)類中有兩個值當(dāng)主線程被外部終止時,會觸發(fā),執(zhí)行的與方法通知下面的鎖操作,主線程正常走完代碼,并最終停止。

spring是如何啟動容器的

常見的一種在本地使用main方法啟動spring的方法

    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
        context.start();
        ...
        //System.in.read(); // 按任意鍵退出
        context.close();
    }
dubbo是如何啟動容器的

這個大家應(yīng)該都知道,通過com.alibaba.dubbo.container.Main.main方法來啟動的。

public class Main {

    //在dubbo.properties中配置, 以配置dubbo.container=log4j,spring為例
    public static final String CONTAINER_KEY = "dubbo.container";

    public static final String SHUTDOWN_HOOK_KEY = "dubbo.shutdown.hook";

    private static final Logger logger = LoggerFactory.getLogger(Main.class);
    
    //整個dubbo,最先使用ExtensionLoader的地方
    private static final ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Container.class);

    private static volatile boolean running = true;

    public static void main(String[] args) {
        try {
            //1. 從dubbo.properties里面讀取dubbo.container這個配置;
            if (args == null || args.length == 0) {
                String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
                args = Constants.COMMA_SPLIT_PATTERN.split(config);
            }
            //2. 使用Container接口的ExtensionLoader中獲取具體的Container實現(xiàn)類;
            final List containers = new ArrayList();
            //agrs中有兩個值 "log4j,spring"
            for (int i = 0; i < args.length; i++) {
                containers.add(loader.getExtension(args[i]));
            }
            logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");

            if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) {
                //5. 當(dāng)主線程被外部終止時,會觸發(fā) shutdownhook,執(zhí)行Container的stop與close方法
                Runtime.getRuntime().addShutdownHook(new Thread() {
                    public void run() {
                        for (Container container : containers) {
                            try {
                                container.stop();
                                logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");
                            } catch (Throwable t) {
                                logger.error(t.getMessage(), t);
                            }
                            synchronized (Main.class) {
                                running = false;
                                //6.通知下面的鎖操作,主線程正常走完代碼,并最終停止。
                                Main.class.notify();
                            }
                        }
                    }
                });
            }
            //3. 執(zhí)行Container接口的start方法;
            for (Container container : containers) {
                container.start();
                logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
            }
            System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");
        } catch (RuntimeException e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
            System.exit(1);
        }
        //4. 用一個死循環(huán),保留主線程;
        synchronized (Main.class) {
            while (running) {
                try {
                    Main.class.wait();
                } catch (Throwable e) {
                }
            }
        }
    }

}
dubbo容器的SPI功能實現(xiàn)

明確下面幾個概念

擴(kuò)展接口 com.alibaba.dubbo.container.Container

擴(kuò)展配置 dubbo.container = log4j,spring

擴(kuò)展實現(xiàn)

com.alibaba.dubbo.container.log4j.Log4jContainer

  log4j的日志初始工作,當(dāng)多進(jìn)程啟動時,做日志隔離

com.alibaba.dubbo.container.logback.LogbackContainer

  logback的日志初始工作

com.alibaba.dubbo.container.spring.SpringContainer

  spring容器的啟動,使用spring容器來實現(xiàn)aop與ioc,**【這個配置,往往是必選的】**

com.alibaba.dubbo.container.jetty.JettyContainer

  啟動一個Servlet Web容器,提供了一個web頁面,做一些監(jiān)控之類的時期,注意:在寫HttpResponse的時候,也是用SPI機制,不同的請 

求頁面經(jīng)過PageServlet交個不同的PageHandler去實現(xiàn)

com.alibaba.dubbo.monitor.simple.RegistryContainer

我們來想一個這樣的問題,上面是dubbo支持的容器,包括log4j、logback、spring、jetty、registry,那么dubbo是如何通過配置的方式來實現(xiàn)容器的可擴(kuò)展的呢?假如給你做你怎么做呢?

spring的API(Application Programming Interface、應(yīng)用編程接口)方式,接口多實現(xiàn)類的動態(tài)調(diào)動;

JDK標(biāo)準(zhǔn)的SPI(Service Provider Interface、)機制
dubbo的擴(kuò)展點加載機制是從JDK的spi機制加強而來。
dubbo改進(jìn)了JDK標(biāo)準(zhǔn)的SPI機制以下問題:

spring與JDK的SPI都會一次性實例化擴(kuò)展點所有實現(xiàn),如果有擴(kuò)展實現(xiàn)初始化很耗時,但如果沒用上,也會加載。

JDK的SPI機制不支持Ioc與Aop功能,而dubbo中的擴(kuò)展點可以直接setter注入其他擴(kuò)展點?!具@個一部分,下面會有涉及,我們會在下一個文章中詳細(xì)描述】

擴(kuò)展接口Container源碼

關(guān)鍵說明,

必須帶有SPI注解

注解里面的值,是默認(rèn)實現(xiàn),在ExtensionLoader源碼去細(xì)講。

/**
 * Container. (SPI, Singleton, ThreadSafe)
 *
 * @author william.liangf
 */
@SPI("spring")
public interface Container {

    /**
     * start.
     */
    void start();

    /**
     * stop.
     */
    void stop();

}
ExtensionLoader源碼

關(guān)鍵說明,

1.  ExtensionLoader有一個private的構(gòu)造函數(shù),并通過getExtensionLoader這個鏡頭方法返回實例,是一個單例工廠類。
2.  一個擴(kuò)展接口對應(yīng)一個ExtensionLoader實例,也就是說最終我們加載了多少個擴(kuò)展接口(注意是擴(kuò)展接口,而不是擴(kuò)展實現(xiàn)類),就多少個實例;
3.  關(guān)鍵static final變量,所有實例共享
    private static final ConcurrentMap, ExtensionLoader> EXTENSION_LOADERS = new ConcurrentHashMap, ExtensionLoader>();

    private static final ConcurrentMap, Object> EXTENSION_INSTANCES = new ConcurrentHashMap, Object>();
4.  所有的final變量,單個實例共享,每一個擴(kuò)展接口對應(yīng)的ExtensionLoader都不一樣
    //擴(kuò)展接口名稱
    private final Class type;
    //也是一個擴(kuò)展接口,用于注入擴(kuò)展接口中需要注入的類,實現(xiàn)dubbo的擴(kuò)展點的自動注入
    private final ExtensionFactory objectFactory;

    private final ConcurrentMap, String> cachedNames = new ConcurrentHashMap, String>();

    private final Holder>> cachedClasses = new Holder>>();

    private final Map cachedActivates = new ConcurrentHashMap();
    private final ConcurrentMap> cachedInstances = new ConcurrentHashMap>();
    private final Holder cachedAdaptiveInstance = new Holder();
    private volatile Class cachedAdaptiveClass = null;
    private String cachedDefaultName;
    private volatile Throwable createAdaptiveInstanceError;

    private Set> cachedWrapperClasses;

    private Map exceptions = new ConcurrentHashMap();

結(jié)合Main類的使用,講一下幾個核心方法

核心方法 -> ExtensionLoader.getExtensionLoader

獲得ExtensionLoader實例

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

獲取ExtensionLoader實例

getExtensionLoader(Container.class)【將返回的實例放到EXTENSION_LOADERS變量中】

new ExtensionLoader(type) 【初始化type與objectFactory變量,初始化objectFactory變量的時候有一點點的繞。假如這個接口不是ExtensionFactory,就需要初始化這樣的一個objectFactory,否則就需要,具體后面會將】

獲取ExtensionLoader實例結(jié)束

核心方法 -> ExtensionLoader.getExtension

獲得擴(kuò)展實現(xiàn)
注意此時已經(jīng)拿到了擴(kuò)展接口Container對應(yīng)的那個ExtensionLoader實例了,在下面的處理中,基本都是更新這個實例的變量,而很少會更新類變量了。

for (int i = 0; i < args.length; i++) {
   containers.add(loader.getExtension(args[i]));
}

getExtension("log4j" or "spring" or "logback" ....)

createExtension("log4j" or "spring" or "logback" ....) --創(chuàng)建指定類型的擴(kuò)展接口的instance

getExtensionClasses() --加載擴(kuò)展接口的所有class文件

loadExtensionClasses() --擴(kuò)展接口的所有的class文件

loadFile() --從三個路徑下,查找class文件

clazz.newInstance() --創(chuàng)建指定class的instance

injectExtension(instace) --注入屬性Ioc

objectFactory.getExtension(pt, property) --反射的方式,解析setXxx(Xxx xxx)方法,注入Xxx實例

injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); --對實例進(jìn)行層層包裝,最終返回一個包裝過后的instance

上面總體邏輯就是
圖片
具體介紹一下loadFile方法

    //...
    private static final String SERVICES_DIRECTORY = "META-INF/services/";
    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
    private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
    //...
    Map> extensionClasses = new HashMap>();
    loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
    loadFile(extensionClasses, DUBBO_DIRECTORY);
    loadFile(extensionClasses, SERVICES_DIRECTORY);
    return extensionClasses;
if (clazz.isAnnotationPresent(Adaptive.class)) {
    if (cachedAdaptiveClass == null) {
        cachedAdaptiveClass = clazz;
    } else if (!cachedAdaptiveClass.equals(clazz)) {
        throw new IllegalStateException("More than 1 adaptive class found: "
                + cachedAdaptiveClass.getClass().getName()
                + ", " + clazz.getClass().getName());
    }
} else {
    try {
        clazz.getConstructor(type);
        Set> wrappers = cachedWrapperClasses;
        if (wrappers == null) {
            cachedWrapperClasses = new ConcurrentHashSet>();
            wrappers = cachedWrapperClasses;
        }
        wrappers.add(clazz);
    } catch (NoSuchMethodException e) {
        clazz.getConstructor();
        if (name == null || name.length() == 0) {
            name = findAnnotationName(clazz);
            if (name == null || name.length() == 0) {
                if (clazz.getSimpleName().length() > type.getSimpleName().length()
                        && clazz.getSimpleName().endsWith(type.getSimpleName())) {
                    name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
                } else {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
                }
            }
        }
        String[] names = NAME_SEPARATOR.split(name);
        if (names != null && names.length > 0) {
            Activate activate = clazz.getAnnotation(Activate.class);
            if (activate != null) {
                cachedActivates.put(names[0], activate);
            }
            for (String n : names) {
                if (!cachedNames.containsKey(clazz)) {
                    cachedNames.put(clazz, n);
                }
                Class c = extensionClasses.get(n);
                if (c == null) {
                    extensionClasses.put(n, clazz);
                } else if (c != clazz) {
                    throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                }
            }
        }
    }
}

從上面三個路徑下加載dubbo擴(kuò)展點的配置。我們以DUBBO_INTERNAL_DIRECTORY路徑下的配置文件為例,說明下dubbo下擴(kuò)展的配置。

擴(kuò)展接口實現(xiàn)類,實現(xiàn)Container接口,例如SpringContainer.java

在資源META-INF.dubbo.internal文件夾下,有一個以Container接口全路徑名稱為名字的文件;

上述文件名中內(nèi)容格式為 {key}={value},key為擴(kuò)展點實現(xiàn)類的配置名稱,例如spring、log4j等;value為SpringContainer類的全路徑名稱

loadFile中就是以這樣的規(guī)則,解析這樣的配置文件,并放到extensionClasses這樣的Map中返回,extensionClasses的key是這個{key},value是這個{value}對應(yīng)的class。

這里面主要是四個邏輯,涉及到幾種情況。
圖片

拿到所有配置的Container實例
for (Container container : containers) {
    container.start();
    logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
}

執(zhí)行SpringContainer.java的start方法

    public void start() {
        String configPath = ConfigUtils.getProperty(SPRING_CONFIG);
        if (configPath == null || configPath.length() == 0) {
            configPath = DEFAULT_SPRING_CONFIG;
        }
        context = new ClassPathXmlApplicationContext(configPath.split("[,s]+"));
        context.start();
    }
這個不是這篇文章最開始的那個問題的答案嘛,原來dubbo就是通過這么簡單的方式的來啟動spring容器的。這算是一個首尾呼應(yīng)嘛~

終于,終于,第一篇文章寫完了~ 下篇文章會講解擴(kuò)展點是如何IOC的。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69058.html

相關(guān)文章

  • 聊聊Dubbo - Dubbo擴(kuò)展機制實戰(zhàn)

    摘要:今天我想聊聊的另一個很棒的特性就是它的可擴(kuò)展性。的擴(kuò)展機制在的官網(wǎng)上,描述自己是一個高性能的框架。接下來的章節(jié)中我們會慢慢揭開擴(kuò)展機制的神秘面紗。擴(kuò)展擴(kuò)展點的實現(xiàn)類。的定義在配置文件中可以看到文件中定義了個的擴(kuò)展實現(xiàn)。 摘要: 在Dubbo的官網(wǎng)上,Dubbo描述自己是一個高性能的RPC框架。今天我想聊聊Dubbo的另一個很棒的特性, 就是它的可擴(kuò)展性。 Dubbo的擴(kuò)展機制 在Dub...

    techstay 評論0 收藏0
  • 聊聊Dubbo - Dubbo擴(kuò)展機制源碼解析

    摘要:什么是類那什么樣類的才是擴(kuò)展機制中的類呢類是一個有復(fù)制構(gòu)造函數(shù)的類,也是典型的裝飾者模式。代碼如下有一個參數(shù)是的復(fù)制構(gòu)造函數(shù)有一個構(gòu)造函數(shù),參數(shù)是擴(kuò)展點,所以它是一個擴(kuò)展機制中的類。 摘要:?在Dubbo可擴(kuò)展機制實戰(zhàn)中,我們了解了Dubbo擴(kuò)展機制的一些概念,初探了Dubbo中LoadBalance的實現(xiàn),并自己實現(xiàn)了一個LoadBalance。是不是覺得Dubbo的擴(kuò)展機制很不錯呀...

    lmxdawn 評論0 收藏0
  • dubbo源碼解析(二)Dubbo擴(kuò)展機制SPI

    摘要:二注解該注解為了保證在內(nèi)部調(diào)用具體實現(xiàn)的時候不是硬編碼來指定引用哪個實現(xiàn),也就是為了適配一個接口的多種實現(xiàn),這樣做符合模塊接口設(shè)計的可插拔原則,也增加了整個框架的靈活性,該注解也實現(xiàn)了擴(kuò)展點自動裝配的特性。 Dubbo擴(kuò)展機制SPI 前一篇文章《dubbo源碼解析(一)Hello,Dubbo》是對dubbo整個項目大體的介紹,而從這篇文章開始,我將會從源碼來解讀dubbo再各個模塊的實...

    DirtyMind 評論0 收藏0
  • Dubbo SPI機制和IOC

    摘要:要構(gòu)建自適應(yīng)實例,先要有自適應(yīng)的實現(xiàn)類,實現(xiàn)類有兩種方式一種通過配置文件,一種是通過是字節(jié)碼的方式動態(tài)生成。 SPI機制 SPI,即(service provider interface)機制,有很多組件的實現(xiàn),如日志、數(shù)據(jù)庫訪問等都是采用這樣的方式,一般通用組件為了提升可擴(kuò)展性,基于接口編程,將操作接口形成標(biāo)準(zhǔn)規(guī)范,但是可以開放多種擴(kuò)展實現(xiàn),這種做法也符合開閉設(shè)計原則,使組件具有可插...

    Scorpion 評論0 收藏0
  • Dubbo Spi機制

    摘要:為了實現(xiàn)在模塊裝配的時候,不在模塊里寫死代碼,就需要一種服務(wù)發(fā)現(xiàn)機制。就提供了這樣一種機制為某個接口尋找服務(wù)實現(xiàn),有點類似思想,將裝配的控制權(quán)移到代碼之外。即接口文件的全類名。五示例遵循上述第一條第點,這里為接口文件,其中和為兩個實現(xiàn)類。 一、Dubbo內(nèi)核 Dubbo內(nèi)核主要包含SPI、AOP、IOC、Compiler。 二、JDK的SPI 1.spi的設(shè)計目標(biāo): 面向?qū)ο蟮脑O(shè)計里...

    mrli2016 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<