国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

dubbo之SPI

UnixAgain / 2061人閱讀

摘要:簡(jiǎn)介全稱(chēng)為,是一種服務(wù)發(fā)現(xiàn)機(jī)制。的本質(zhì)是將接口實(shí)現(xiàn)類(lèi)的全限定名配置在文件中,并由服務(wù)加載器讀取配置文件,加載實(shí)現(xiàn)類(lèi)。不過(guò),并未使用原生的機(jī)制,而是對(duì)其進(jìn)行了增強(qiáng),使其能夠更好的滿(mǎn)足需求。并未使用,而是重新實(shí)現(xiàn)了一套功能更強(qiáng)的機(jī)制。

1、SPI簡(jiǎn)介

SPI 全稱(chēng)為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機(jī)制。SPI 的本質(zhì)是將接口實(shí)現(xiàn)類(lèi)的全限定名配置在文件中,并由服務(wù)加載器讀取配置文件,加載實(shí)現(xiàn)類(lèi)。這樣可以在運(yùn)行時(shí),動(dòng)態(tài)為接口替換實(shí)現(xiàn)類(lèi)。正因此特性,我們可以很容易的通過(guò) SPI 機(jī)制為我們的程序提供拓展功能。SPI 機(jī)制在第三方框架中也有所應(yīng)用,比如 Dubbo 就是通過(guò) SPI 機(jī)制加載所有的組件。不過(guò),Dubbo 并未使用 Java 原生的 SPI 機(jī)制,而是對(duì)其進(jìn)行了增強(qiáng),使其能夠更好的滿(mǎn)足需求。在 Dubbo 中,SPI 是一個(gè)非常重要的模塊。基于 SPI,我們可以很容易的對(duì) Dubbo 進(jìn)行拓展。如果大家想要學(xué)習(xí) Dubbo 的源碼,SPI 機(jī)制務(wù)必弄懂。接下來(lái),我們先來(lái)了解一下 Java SPI 與 Dubbo SPI 的用法,然后再來(lái)分析 Dubbo SPI 的源碼。

2、SPI示例 2.1 Java SPI示例

本節(jié)通過(guò)一個(gè)示例演示 Java SPI 的使用方法。首先,我們定義一個(gè)接口,名稱(chēng)為 HelloService。

public interface HelloService {
    void sayHello();
}

家下來(lái)定義兩個(gè)實(shí)現(xiàn)類(lèi):HelloAService、HelloBService;

public class HelloAService implements HelloService {
    
    @Override
    public void sayHello() {
        System.out.println("Hello, I am A");
    }
}

public class HelloBService implements HelloService {

    @Override
    public void sayHello() {
        System.out.println("Hello, I am B");
    }
}

接下來(lái) META-INF/services 文件夾下創(chuàng)建一個(gè)文件,名稱(chēng)為 Robot 的全限定名 org.apache.spi.Robot。文件內(nèi)容為實(shí)現(xiàn)類(lèi)的全限定的類(lèi)名,如下:

org.apache.spi.HelloAService
org.apache.spi.HelloBService

做好所需的準(zhǔn)備工作,接下來(lái)編寫(xiě)代碼進(jìn)行測(cè)試

public class JavaSPITest {

    @Test
    public void sayHello() throws Exception {
        ServiceLoader serviceLoader = ServiceLoader.load(HelloService.class);
        System.out.println("Java SPI");
        serviceLoader.forEach(HelloService::sayHello);
    }
}

最后結(jié)果如下:

Java SPI
Hello, I am A
Hello, I am B

從測(cè)試結(jié)果可以看出,我們的兩個(gè)實(shí)現(xiàn)類(lèi)被成功的加載,并輸出了相應(yīng)的內(nèi)容。關(guān)于 Java SPI 的演示先到這里,接下來(lái)演示 Dubbo SPI。

2.2 Dubbo SPI

Dubbo 并未使用 Java SPI,而是重新實(shí)現(xiàn)了一套功能更強(qiáng)的 SPI 機(jī)制。Dubbo SPI 的相關(guān)邏輯被封裝在了 ExtensionLoader 類(lèi)中,通過(guò) ExtensionLoader,我們可以加載指定的實(shí)現(xiàn)類(lèi)。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路徑下,配置內(nèi)容如下。

helloAService = org.apache.spi.HelloAService
helloBService = org.apache.spi.HelloBService

與 Java SPI 實(shí)現(xiàn)類(lèi)配置不同,Dubbo SPI 是通過(guò)鍵值對(duì)的方式進(jìn)行配置,這樣我們可以按需加載指定的實(shí)現(xiàn)類(lèi)。另外,在測(cè)試 Dubbo SPI 時(shí),需要在 Robot 接口上標(biāo)注 @SPI 注解。下面來(lái)演示 Dubbo SPI 的用法:

public class DubboSPITest {

    @Test
    public void sayHello() throws Exception {
        ExtensionLoader extensionLoader = 
            ExtensionLoader.getExtensionLoader(HelloService.class);
        HelloService a = extensionLoader.getExtension("HelloAService");
        a.sayHello();
        HelloService b = extensionLoader.getExtension("HelloBService");
        b.sayHello();
    }
}

測(cè)試結(jié)果如下:

Java SPI
Hello, I am A
Hello, I am B
3、Dubbo SPI源碼解析

Dubbo SPI相關(guān)邏輯都在ExtensionLoader 類(lèi)中,首先通過(guò)getExtensionLoader獲取一個(gè)ExtensionLoader實(shí)例,然后在根據(jù)getExtension獲取type的擴(kuò)展類(lèi)。
getExtensionLoader方法比較簡(jiǎn)單,先從緩存中獲取,如果緩存不存在,則創(chuàng)建ExtensionLoader對(duì)象,并存入緩存中,在看getExtension方法:

    public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        // 根據(jù)擴(kuò)展名從緩存中獲取,緩存中沒(méi)有,創(chuàng)建并存入緩存
        Holder holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder());
            holder = cachedInstances.get(name);
        }
        Object instance = holder.get();
        // 雙重檢查
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    // 根據(jù)擴(kuò)展名創(chuàng)建實(shí)例對(duì)象
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

上面代碼的邏輯比較簡(jiǎn)單,首先檢查緩存,緩存未命中則創(chuàng)建拓展對(duì)象。下面我們來(lái)看一下創(chuàng)建拓展對(duì)象的過(guò)程是怎樣的。

    private T createExtension(String name) {
        // 從配置文件中加載所有的拓展類(lèi),可得到“配置項(xiàng)名稱(chēng)”到“配置類(lèi)”的映射關(guān)系表
        Class clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            injectExtension(instance);
            Set> wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class wrapperClass : wrapperClasses) {
                    // 將當(dāng)前 instance 作為參數(shù)傳給 Wrapper 的構(gòu)造方法,并通過(guò)反射創(chuàng)建 Wrapper 實(shí)例。
                    // 然后向 Wrapper 實(shí)例中注入依賴(lài),最后將 Wrapper 實(shí)例再次賦值給 instance 變量
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

我們?cè)谕ㄟ^(guò)名稱(chēng)獲取拓展類(lèi)之前,首先需要根據(jù)配置文件解析出拓展項(xiàng)名稱(chēng)到拓展類(lèi)的映射關(guān)系表(Map<名稱(chēng), 拓展類(lèi)>),之后再根據(jù)拓展項(xiàng)名稱(chēng)從映射關(guān)系表中取出相應(yīng)的拓展類(lèi)即可。相關(guān)過(guò)程的代碼分析如下

    private Map> getExtensionClasses() {
        // 從緩存中獲取
        Map> classes = cachedClasses.get();
        // 雙重檢查
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    // 加載所有的擴(kuò)展類(lèi)
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

getExtensionClasses方法同樣是先從緩存中讀取,緩存不存在,在去加載:

    private Map> loadExtensionClasses() {
        // 獲取擴(kuò)展類(lèi)SPI注解
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if ((value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                if (names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                if (names.length == 1) {
                    cachedDefaultName = names[0];
                }
            }
        }

        Map> extensionClasses = new HashMap>();
        // 加載指定文件夾下的配置文件
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        return extensionClasses;
    }

loadExtensionClasses 方法總共做了兩件事情,一是對(duì) SPI 注解進(jìn)行解析,二是調(diào)用 loadDirectory 方法加載指定文件夾配置文件。SPI 注解解析過(guò)程比較簡(jiǎn)單,無(wú)需多說(shuō)。下面我們來(lái)看一下 loadDirectory 做了哪些事情。

    private void loadDirectory(Map> extensionClasses, String dir, String type) {
        // fileName = 文件夾路徑 + type 全限定名 
        String fileName = dir + type;
        try {
            Enumeration urls;
            ClassLoader classLoader = findClassLoader();
            if (classLoader != null) {
                // 根據(jù)文件名加載所有的同名文件
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
                    // 加載資源
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

loadDirectory 方法先通過(guò) classLoader 獲取所有資源鏈接,然后再通過(guò) loadResource 方法加載資源。我們繼續(xù)跟下去,看一下 loadResource 方法的實(shí)現(xiàn)。

    private void loadResource(Map> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
            try {
                String line;
                // 讀取文件中內(nèi)容
                while ((line = reader.readLine()) != null) {
                    final int ci = line.indexOf("#");
                    if (ci >= 0) {
                        line = line.substring(0, ci);
                    }
                    line = line.trim();
                    if (line.length() > 0) {
                        try {
                            String name = null;
                            int i = line.indexOf("=");
                            if (i > 0) {
                                // 以等于號(hào) = 為界,截取鍵與值
                                name = line.substring(0, i).trim();
                                line = line.substring(i + 1).trim();
                            }
                            if (line.length() > 0) {
                                // 加載類(lèi),并通過(guò) loadClass 方法對(duì)類(lèi)進(jìn)行緩存
                                loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                            }
                        } catch (Throwable t) {
                            IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                            exceptions.put(line, e);
                        }
                    }
                }
            } finally {
                reader.close();
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", class file: " + resourceURL + ") in " + resourceURL, t);
        }
    }

loadClass()方法主要是利用反射原理,根據(jù)類(lèi)的權(quán)限定名加載成類(lèi),并存入緩存中

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/73240.html

相關(guān)文章

  • dubboSPI自適應(yīng)擴(kuò)展機(jī)制

    摘要:對(duì)于這個(gè)矛盾的問(wèn)題,通過(guò)自適應(yīng)拓展機(jī)制很好的解決了。自適應(yīng)拓展機(jī)制的實(shí)現(xiàn)邏輯比較復(fù)雜,首先會(huì)為拓展接口生成具有代理功能的代碼。 1、背景 在 Dubbo 中,很多拓展都是通過(guò) SPI 機(jī)制進(jìn)行加載的,比如 Protocol、Cluster、LoadBalance 等。有時(shí),有些拓展并不想在框架啟動(dòng)階段被加載,而是希望在拓展方法被調(diào)用時(shí),根據(jù)運(yùn)行時(shí)參數(shù)進(jìn)行加載。這聽(tīng)起來(lái)有些矛盾。拓展未被...

    vvpale 評(píng)論0 收藏0
  • 聊聊Dubbo - Dubbo可擴(kuò)展機(jī)制源碼解析

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

    lmxdawn 評(píng)論0 收藏0
  • 聊聊Dubbo - Dubbo可擴(kuò)展機(jī)制實(shí)戰(zhàn)

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

    techstay 評(píng)論0 收藏0
  • dubbo源碼解析(二)Dubbo擴(kuò)展機(jī)制SPI

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

    DirtyMind 評(píng)論0 收藏0
  • Dubbo Spi機(jī)制

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

    mrli2016 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

UnixAgain

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<