欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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)文章

  • java括號匹配算法求解(用棧實現(xiàn))

    java括號匹配算法求解(用棧實現(xiàn))

    這篇文章主要介紹了java括號匹配算法求解(用棧實現(xiàn)),需要的朋友可以參考下
    2020-12-12
  • Java反射學(xué)習(xí) getClass()函數(shù)應(yīng)用

    Java反射學(xué)習(xí) getClass()函數(shù)應(yīng)用

    所謂反射,可以理解為在運行時期獲取對象類型信息的操作,本文將詳細介紹,需要的朋友可以參考下
    2012-12-12
  • 解讀@RequestBody與post請求的關(guān)系

    解讀@RequestBody與post請求的關(guān)系

    這篇文章主要介紹了解讀@RequestBody與post請求的關(guān)系,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • SpringCloud Netfilx Ribbon負載均衡工具使用方法介紹

    SpringCloud Netfilx Ribbon負載均衡工具使用方法介紹

    Ribbon是Netflix的組件之一,負責(zé)注冊中心的負載均衡,有助于控制HTTP和TCP客戶端行為。Spring Cloud Netflix Ribbon一般配合Ribbon進行使用,利用在Eureka中讀取的服務(wù)信息,在調(diào)用服務(wù)節(jié)點時合理進行負載
    2022-12-12
  • Java可變參數(shù)列表詳解

    Java可變參數(shù)列表詳解

    這篇文章主要介紹了Java可變參數(shù)列表詳解的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • java代碼mqtt接收發(fā)送消息方式

    java代碼mqtt接收發(fā)送消息方式

    這篇文章主要介紹了java代碼mqtt接收發(fā)送消息方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Java接口統(tǒng)一樣式返回模板簡介

    Java接口統(tǒng)一樣式返回模板簡介

    這篇文章主要介紹了Java接口統(tǒng)一樣式返回模板簡介,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • 解決springboot啟動成功,但訪問404的問題

    解決springboot啟動成功,但訪問404的問題

    這篇文章主要介紹了解決springboot啟動成功,但訪問404的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Spring?Boot中使用Swagger3.0.0版本構(gòu)建RESTful?APIs的方法

    Spring?Boot中使用Swagger3.0.0版本構(gòu)建RESTful?APIs的方法

    Swagger?是一個規(guī)范和完整的框架,用于生成、描述、調(diào)用和可視化?RESTful?風(fēng)格的?Web?服務(wù),這篇文章主要介紹了Spring?Boot中使用Swagger3.0.0版本構(gòu)建RESTful?APIs的方法,需要的朋友可以參考下
    2022-11-11
  • jdk線程池的實現(xiàn)

    jdk線程池的實現(xiàn)

    本文主要介紹了jdk線程池的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04

最新評論