最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java SPI用法案例詳解

 更新時間:2021年09月02日 09:15:01   作者:jy的blog  
這篇文章主要介紹了Java SPI用法案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下

1.什么是SPI

     SPI全稱Service Provider Interface,是Java提供的一套用來被第三方實現(xiàn)或者擴展的接口,它可以用來啟用框架擴展和替換組件。 SPI的作用就是為這些被擴展的API尋找服務(wù)實現(xiàn)。

2.SPI和API的使用場景

    API (Application Programming Interface)在大多數(shù)情況下,都是實現(xiàn)方制定接口并完成對接口的實現(xiàn),調(diào)用方僅僅依賴接口調(diào)用,且無權(quán)選擇不同實現(xiàn)。 從使用人員上來說,API 直接被應(yīng)用開發(fā)人員使用。

    SPI (Service Provider Interface)是調(diào)用方來制定接口規(guī)范,提供給外部來實現(xiàn),調(diào)用方在調(diào)用時則選擇自己需要的外部實現(xiàn)。  從使用人員上來說,SPI 被框架擴展人員使用。

3.SPI的簡單實現(xiàn)

    下面我們來簡單實現(xiàn)一個jdk的SPI的簡單實現(xiàn)。

    首先第一步,定義一組接口:

public interface UploadCDN {
    void upload(String url);
}

   這個接口分別有兩個實現(xiàn):

public class QiyiCDN implements UploadCDN {  //上傳愛奇藝cdn
    @Override
    public void upload(String url) {
        System.out.println("upload to qiyi cdn");
    }
}

public class ChinaNetCDN implements UploadCDN {//上傳網(wǎng)宿cdn
    @Override
    public void upload(String url) {
        System.out.println("upload to chinaNet cdn");
    }
}

    然后需要在resources目錄下新建META-INF/services目錄,并且在這個目錄下新建一個與上述接口的全限定名一致的文件,在這個文件中寫入接口的實現(xiàn)類的全限定名:

    這時,通過serviceLoader加載實現(xiàn)類并調(diào)用:

public static void main(String[] args) {
    ServiceLoader<UploadCDN> uploadCDN = ServiceLoader.load(UploadCDN.class);
    for (UploadCDN u : uploadCDN) {
        u.upload("filePath");
    }
}

    輸出如下:

     這樣一個簡單的spi的demo就完成了。可以看到其中最為核心的就是通過ServiceLoader這個類來加載具體的實現(xiàn)類的。

4. SPI原理解析

     通過上面簡單的demo,可以看到最關(guān)鍵的實現(xiàn)就是ServiceLoader這個類,可以看下這個類的源碼,如下:

public final class ServiceLoader<S> implements Iterable<S> {


    //掃描目錄前綴
    private static final String PREFIX = "META-INF/services/";

    // 被加載的類或接口
    private final Class<S> service;

    // 用于定位、加載和實例化實現(xiàn)方實現(xiàn)的類的類加載器
    private final ClassLoader loader;

    // 上下文對象
    private final AccessControlContext acc;

    // 按照實例化的順序緩存已經(jīng)實例化的類
    private LinkedHashMap<String, S> providers = new LinkedHashMap<>();

    // 懶查找迭代器
    private java.util.ServiceLoader.LazyIterator lookupIterator;

    // 私有內(nèi)部類,提供對所有的service的類的加載與實例化
    private class LazyIterator implements Iterator<S> {
        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        String nextName = null;

        //...
        private boolean hasNextService() {
            if (configs == null) {
                try {
                    //獲取目錄下所有的類
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    //...
                }
                //....
            }
        }

        private S nextService() {
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                //反射加載類
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
            }
            try {
                //實例化
                S p = service.cast(c.newInstance());
                //放進緩存
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                //..
            }
            //..
        }
    }
}

     上面的代碼只貼出了部分關(guān)鍵的實現(xiàn),有興趣的讀者可以自己去研究,下面貼出比較直觀的spi加載的主要流程供參考:

5.dubbo SPI

    dubbo作為一個高度可擴展的rpc框架,也依賴于java的spi,并且dubbo對java原生的spi機制作出了一定的擴展,使得其功能更加強大。

首先,從上面的java spi的原理中可以了解到,java的spi機制有著如下的弊端:

  • 只能遍歷所有的實現(xiàn),并全部實例化。
  • 配置文件中只是簡單的列出了所有的擴展實現(xiàn),而沒有給他們命名。導(dǎo)致在程序中很難去準確的引用它們。
  • 擴展如果依賴其他的擴展,做不到自動注入和裝配。
  • 擴展很難和其他的框架集成,比如擴展里面依賴了一個Spring bean,原生的Java SPI不支持。

 dubbo的spi有如下幾個概念:

    (1)擴展點:一個接口。

    (2)擴展:擴展(接口)的實現(xiàn)。

    (3)擴展自適應(yīng)實例:其實就是一個Extension的代理,它實現(xiàn)了擴展點接口。在調(diào)用擴展點的接口方法時,會根據(jù)實際的參數(shù)來決定要使用哪個擴展。dubbo會根據(jù)接口中的參數(shù),自動地決定選擇哪個實現(xiàn)。

    (4)@SPI:該注解作用于擴展點的接口上,表明該接口是一個擴展點。

    (5)@Adaptive:@Adaptive注解用在擴展接口的方法上。表示該方法是一個自適應(yīng)方法。Dubbo在為擴展點生成自適應(yīng)實例時,如果方法有@Adaptive注解,會為該方法生成對應(yīng)的代碼。

    dubbo的spi也會從某些固定的路徑下去加載配置文件,并且配置的格式與java原生的不一樣,類似于property文件的格式:

     下面將基于dubbo去實現(xiàn)一個簡單的擴展實現(xiàn)。首先,要實現(xiàn)LoadBalance這個接口,當(dāng)然這個接口是被注解標注的可以擴展的:

@SPI("random")
public interface LoadBalance {
    @Adaptive({"loadbalance"})
    <T> Invoker<T> select(List<Invoker<T>> var1, URL var2, Invocation var3) throws RpcException;
}

public class DemoLoadBalance implements LoadBalance {

    @Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
        System.out.println("my demo loadBalance is used, hahahahh");
        return invokers.get(0);//選擇第一個
    }
}

     然后,需要在duboo SPI的掃描目錄下,添加配置文件,注意配置文件的名稱要和擴展點的接口名稱對應(yīng)起來:

     還需要在dubbo的spring配置中顯式的聲明,使用上面自己實現(xiàn)的負載均衡策略:

<dubbo:reference id="helloService" interface="com.dubbo.spi.demo.api.IHelloService" loadbalance="demo" />

    然后,啟動dubbo,調(diào)用service,就可以發(fā)現(xiàn)確實是使用了自定義的負載策略:

     至此,dubbo的spi的demo也完成了。

    dubbo spi的原理和jdk的實現(xiàn)稍有不同,大概流程如下圖,具體的實現(xiàn)讀者可以自己了解下源碼。

6.總結(jié)

    關(guān)于spi的詳解到此就結(jié)束了,總結(jié)下spi能帶來的好處:

  • 不需要改動源碼就可以實現(xiàn)擴展,解耦。
  • 實現(xiàn)擴展對原來的代碼幾乎沒有侵入性。
  • 只需要添加配置就可以實現(xiàn)擴展,符合開閉原則。

到此這篇關(guān)于Java SPI用法案例詳解的文章就介紹到這了,更多相關(guān)Java SPI用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

武安市| 晋中市| 洪湖市| 鄯善县| 上饶市| 山东省| 左贡县| 察雅县| 六安市| 八宿县| 吉安县| 扶沟县| 绥中县| 乌兰浩特市| 寿阳县| 克拉玛依市| 庆城县| 凌源市| 微山县| 龙南县| 漳州市| 沾益县| 怀仁县| 驻马店市| 皮山县| 凉城县| 昭平县| 万年县| 柳江县| 稻城县| 长子县| 紫金县| 灯塔市| 东港市| 凤阳县| 宿迁市| 宜春市| 南木林县| 永仁县| 临沧市| 县级市|