国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Dubbo中SPI機制的實現原理和優勢

開發 前端
我們從 Dubbo 配置項的定義中發現,Dubbo 采用了與 JDK 不同的實現機制。雖然 Dubbo 也采用了 SPI 機制,也是從 jar 包中動態加載實現類,但它的實現方式與 JDK 中基于 ServiceLoader 是不一樣的。

確保系統的擴展性是我們開展架構設計工作的核心目標之一。實現擴展性的方法有很多,JDK 本身內置了一個 SPI(Service Provider Interface,服務提供者接口)機制,來幫開發人員動態加載各種不同的實現類,只要這些實現類遵循一定的開發規范即可。

另一方面,JDK 自帶的 SPI 機制存在一定的缺陷,因此市面上有些框架對 JDK 中的 SPI 機制做了一些增強,這方面的代表性框架就是 Dubbo。在今天的課程中,我們將對這兩種 SPI 機制進行對比,并重點闡述 Dubbo 中 SPI 機制的實現原理和優勢。為了更好地做比較,讓我們先從 JDK 中的 SPI 機制講起。

JDK 中的 SPI 機制解析

如果我們采用 JDK 中的 SPI,具體的開發工作會涉及三個步驟。

實現 JDK SPI 機制的開發步驟實現 JDK SPI 機制的開發步驟

對于 SPI 的開發者而言,我們需要設計一個服務接口,然后根據業務場景提供不同的實現類,這是第一步。

接下來的第二步是關鍵,我們需要創建一個以服務接口命名的配置文件,并把這個文件放置到代碼工程的 META-INF/services 目錄下。請注意,在這個配置文件中,我們需要指定服務接口對應實現類的完整類名。通過這一步,我們可以得到了一個包含 SPI 類和配置的 jar 包。

最后,SPI 的使用者就可以加載這個 jar 包并找到其中的這個配置文件,并根據所配置的實現類完整類名對這些類進行實例化。

上圖中的后面兩個步驟實際上都是為了遵循 JDK 中 SPI 的實現機制而進行的配置工作。

為了實現對 SPI 實現類的動態記載,JDK 專門提供了一個 ServiceLoader 工具類,這個工具類的使用方法如下所示:

public static void main(String[] args) {
  ServiceLoader<LogProvider> loader = ServiceLoader.load(LogProvider.class);
  for (LogProvider provider : loader) {
   System.out.println(provider.getClass());
   provider.info(“testInfo”);
  }
}

這里有一個 LogProvider 接口,并通過 ServiceLoader 的 load 方法將這個接口所配置的實現類加載到內存中,從而可以方便地使用這些 SPI 實現類所提供的功能。

接下來,讓我們來分析一下這個 ServiceLoader 工具類的實現原理。

ServiceLoader 本身實現了 JDK 中的 Iterable 接口,因此在上面的代碼示例中,通過 ServiceLoader.load 方法我們獲取的是一個迭代器,而底層則用到了 ServiceLoader.LazyIterator 這個迭代器類。

從命名上看,LazyIterator 是一個具備延遲加載機制的迭代器,它有 hasNextService 和 nexServicet 這兩個核心方法。我們先來看 hasNextService 方法:

//配置文件路徑
static final String PREFIX = "META-INF/services/";
private boolean hasNextService() {
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        // 通過 PREFIX 前綴與服務接口的名稱,我們可以找到目標 SPI 配置文件
        String fullName = PREFIX + service.getName();
        // 加載配置文件
        if (loader == null)
            configs = ClassLoader.getSystemResources(fullName);
        else
            configs = loader.getResources(fullName);
    }
    // 對 SPI 配置文件進行遍歷,并解析配置內容
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return false;
        }
        // 解析配置文件
        pending = parse(service, configs.nextElement());
 }
 // 更新 nextName 字段
    nextName = pending.next();
    return true;
}

可以看到,hasNextService 方法的核心作用是找到并解析配置文件。而接下來要展開的 nextService 方法則負責對所配置的類進行實例化,核心實現如下所示:

private S nextService() {
    String cn = nextName;
    nextName = null;
    // 加載 nextName 字段指定的類
 Class<?> c = Class.forName(cn, false, loader);
 // 檢測類型
    if (!service.isAssignableFrom(c)) {
        fail(service, "Provider " + cn  + " not a subtype");
 }
 // 創建實現類的對象
 S p = service.cast(c.newInstance());
 // 緩存已創建的對象
 providers.put(cn, p);
    return p;
}

這里通過 newInstance 方法創建了目標實例,并將已創建的實例對象放到 providers 集合中進行緩存,從而提高訪問效率。

Dubbo 中的 SPI 機制解析

為了實現框架自身的擴展性,Dubbo 也采用了類似 JDK 中 SPI 的設計思想,但提供了一套新的實現方式,并添加了一些擴展功能。

Dubbo 中與 SPI 機制相關的注解主要包括@SPI、@Adaptive 和@Activate,其中@SPI 注解提供了與 JDK 中 SPI 類似的功能。

Dubbo 中 SPI 相關注解Dubbo 中 SPI 相關注解

這三個注解的應用場景各不相同,其中@SPI 注解為 Dubbo 提供了最基礎的 SPI 機制,而@Adaptive 和@Activate 注解都是構建在這個注解之上,因此我們重點介紹@SPI 注解。如果在某個接口上添加了這個注解,那么 Dubbo 在運行過程中就會去查找接口對應的擴展點實現。

在 Dubbo 中,隨處可以看到@SPI 注解的應用場景。舉個例子,Protocol 接口定義如下:

@SPI("dubbo")
public interface Protocol

可以看到,在這個接口上使用的就是@SPI(“dubbo”) 注解。

請注意,在@SPI 注解中可以指定默認擴展點的名稱,例如這里的“dubbo”用來表明在 Protocol 接口的所有實現類中,DubboProtocol 是它的默認實現。

有了 SPI 的定義,我們接下來看一看 Dubbo 中 SPI 配置信息的存儲方式。我們已經知道,JDK 只會把 SPI 配置存放在 META-INF/services/這個目錄下,而 Dubbo 則提供了三個類似這樣的目錄:

Dubbo 中 SPI 配置的存放目錄Dubbo 中 SPI 配置的存放目錄

作為示例,我們繼續圍繞上面提到的 Protocol 接口展開討論。

針對 Protocol 接口,Dubbo 提供了 gRPCProtocol、DubboProtocol 等多個實現類,并通過 SPI 機制完成對具體某種實現方案的加載過程。讓我們分別來到提供這些實現類的代碼工程 dubbo-rpc-grpc 和 dubbo-rpc-dubbo,會發現在 META-INF/dubbo/internal/目錄下都包含了一個 com.apache.dubbo.rpc.Protocol 配置文件。其中,dubbo-rpc-grpc 工程的代碼結構如圖所示:

dubbo-rpc-grpc 工程的代碼結構dubbo-rpc-grpc 工程的代碼結構

類似的,dubbo-rpc-dubbo 工程的代碼結構如下圖所示:

dubbo-rpc-dubbo 工程的代碼結構dubbo-rpc-dubbo 工程的代碼結構

我們分別打開這兩個工程的 com.apache.dubbo.rpc.Protocol 配置文件,可以發現它們分別指向了 org.apache.dubbo.rpc.protocol.grpc.GrpcProtocol 和 org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol 類。

//dubbo-rpc-grpc 工程
grpc=org.apache.dubbo.rpc.protocol.grpc.GrpcProtocol
//dubbo-rpc-dubbo 工程:
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol

當 Dubbo 在引用具體某一個代碼工程時,就可以通過這個工程中的配置項就可以找到 Dubbo 接口對應的擴展點實現。

同時,我們從上面配置項中也可以看出,Dubbo 中采用的定義方式與 JDK 中的不一樣。Dubbo 使用的一個 Key 值(如上面的 gRPC 和 Dubbo)來指定具體的配置項名稱,而不是采用完整類路徑。

介紹完@SPI 注解,我們接下來看 Dubbo 中的 ExtensionLoader 類,這個類扮演著與 JDK 中 ServiceLoader 工具類相同的角色。ExtensionLoader 是實現擴展點加載的核心類,如果我們想要獲取 DubboProtocol 這個實現類,那么可以采用以下方式:

DubboProtocol dubboProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(DubboProtocol.NAME);

我們來看一下這里 getExtension 方法的細節,這個方法代碼如下所示:

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 方法進行創建。我們一路跟蹤 createExtension 方法,終于看到了熟悉的 SPI 機制,如下所示:

private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
             //確定緩存名稱
        }
        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
     //分別從三個目錄中加載類實例
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
}

在這里,我們調用了三次 loadFile 方法,分別在 META-INF/dubbo/、META-INF/services/和 META-INF/dubbo/internal/這三個目錄中加載擴展點。在 loadFile 方法中,Dubbo 是直接通過 Class.forName 的形式加載這些 SPI 的擴展類,并進行緩存。

講到這里,我們發現,為了提升實例類的加載速度,Dubbo 和 JDK 都采用了緩存機制,這是它們的一個共同點。但實際上,我們也已經可以梳理 Dubbo 中 SPI 機制與 JDK 中 SPI 機制的區別,核心有兩點,就是 配置文件位置和 獲取實現類的條件

Dubbo 與 JDK 中 SPI 機制的兩點核心區別Dubbo 與 JDK 中 SPI 機制的兩點核心區別

  • 從加載 SPI 實例的配置文件位置來看,Dubbo 支持更多的加載路徑。JDK 只能加載一個固定的 META-INF/services,而 Dubbo 有三個路徑。
  • 就獲取實現類的條件而言,Dubbo 采用的是直接通過名稱對應的 Key 值來定位具體實現類,而 ServiceLoader 內部使用的是一個迭代器,在獲取目標接口的實現類時,只能通過遍歷的方式把配置文件中的類全部加載并實例化,顯然這樣效率比較低下。

簡單來說,Dubbo 沒有直接沿用 JDK SPI 機制,而是自己實現一套的主要目的就是克服這種效率低下的情況,并提供了更多的靈活性。

總結

我們從 Dubbo 配置項的定義中發現,Dubbo 采用了與 JDK 不同的實現機制。雖然 Dubbo 也采用了 SPI 機制,也是從 jar 包中動態加載實現類,但它的實現方式與 JDK 中基于 ServiceLoader 是不一樣的。于是,我們詳細分析了 JDK 和 Dubbo 在 SPI 機制設計和實現上的差異,并闡明了 Dubbo 內部的實現原理和所具備的優勢。

責任編輯:武曉燕 來源: 程序員技術充電站
相關推薦

2024-10-29 08:34:55

SPI機制接口

2025-05-08 03:25:00

DubboSPI機制

2021-09-10 08:31:19

DubboSPI框架

2020-12-14 11:35:22

SPI Java機制

2023-12-11 07:21:12

SPI機制插件

2025-03-04 09:02:25

JavaSPI機制

2024-01-15 08:25:53

SPI機制JavaDubbo

2018-07-06 15:30:14

DubboSPIJDK

2023-03-13 22:09:59

JavaSpring機制

2020-06-30 15:35:36

JavaSPI代碼

2023-02-15 13:57:13

JavaSPI動態擴展

2011-11-30 14:35:19

JavaSPI

2025-08-05 01:55:00

JavaSPI機制

2025-05-27 01:00:00

2011-06-13 10:21:25

QT 信號 槽機制

2020-11-20 07:51:02

JavaSPI機制

2010-04-27 12:56:35

lvs負載均衡

2023-06-05 08:07:33

JavaJava SPI

2023-01-30 22:43:39

DubboZooKeeper

2024-01-19 12:48:00

Redis存儲數據庫
點贊
收藏

51CTO技術棧公眾號

欧美精品影院| 亚洲免费色视频| 国产精品99一区| 欧美黑粗硬大| 亚洲国语精品自产拍在线观看| 亚洲精品视频在线免费| 国产日韩欧美不卡| 无码人妻丰满熟妇区96| 国产精品影视在线观看| 99热一区二区三区| 国内精品久久久久影院色| 久久本道综合色狠狠五月| 欧美jizzhd精品欧美巨大免费| 国产精品久久久久影院日本| 国产成人高清| 国产精品免费久久久| 欧美综合一区| 亚洲一区二区三| 亚洲成人一区| 亚洲综合在线中文字幕| 欧美一区综合| 久久免费看av| 蜜臀va亚洲va欧美va天堂 | 国产高清不卡二三区| 91香蕉视频网址| 国产一区二区看久久| 成人免费a级片| 久久精品人人做| 国产天堂在线观看| 欧美午夜片欧美片在线观看| 在线观看免费高清完整| 精品国产乱码久久久久久浪潮| 6699嫩草久久久精品影院| 亚洲欧美综合v| 日韩一区二区三区在线看| 日韩美女主播视频| 国产综合亚洲精品一区二| 日韩精品一区二区三区色偷偷 | 国产盗摄在线视频网站| 亚洲女成人图区| 欧美日本三级| 国产精品久久中文| 每日更新成人在线视频| 欧美中日韩在线| 亚洲婷婷综合色高清在线| 在线国产小视频| 亚洲精品aⅴ中文字幕乱码| 韩国三级大全久久网站| 国产精品美女久久| 久久婷婷麻豆| 欧美少妇性生活视频| 欧美日韩国产麻豆| 国产成人免费在线| 亚洲国产一二三| 俄罗斯一级**毛片在线播放| 一区二区三区日韩欧美| 免费欧美一级视频| 日本免费新一区视频| 国产精品一区二区久久| 欧美va天堂在线| 国产女呦网站| 日韩中文字幕在线看| 日韩国产在线一| 久久天天久久| 91产国在线观看动作片喷水| 五月天av在线| 日韩高清中文字幕| 国产无遮挡裸体视频在线观看| 日韩激情片免费| 一区二区三区四区五区在线| 欧美日韩精品免费观看| 果冻天美麻豆一区二区国产| 欧美精品一区二区三区免费播放| 亚洲国产精品视频| 色综合天天色| 久艹在线免费观看| 亚洲精品大尺度| 欧美少妇性生活视频| 美女网站视频一区| 性爱视频在线播放| 欧美午夜精品久久久久免费视| 欧美三区免费完整视频在线观看| 在线一区二区三区视频| 看一级黄色录像| 国产日韩欧美不卡在线| 91视频免费版污| 97超碰最新| 色爱av综合网| 国产三级在线看| 欧美午夜精品久久久| 国产精品视频精品视频| 激情综合网激情| 亚洲男女网站| 亚洲一级特黄| 精品999日本久久久影院| 邪恶网站在线观看| gogo在线观看| 成人短剧在线观看| 欧美一区二区三区思思人 | 欧美日本不卡视频| 一区二区三区成人精品| 欧美日韩精品一区| 亚洲欧美日韩久久| 欧美日韩国产v| 99理论电影网| 亚洲精品一二三四区| 精品国产一区二区三区2021| 久久久久久a亚洲欧洲aⅴ| 美女视频黄免费的久久| 亚洲免费一区| 巨大荫蒂视频欧美另类大| 亚洲熟妇av一区二区三区漫画| 韩国三级日本三级少妇99| 九色精品美女在线| 精品va天堂亚洲国产| japanese23hdxxxx日韩| 国产午夜福利在线播放| 国产精品爱久久久久久久| 日韩精品影音先锋| 一区二区三区四区中文字幕| 美女精品一区| 欧美偷拍自拍| 亚洲国产91视频| 国产写真视频在线观看| 2222www色视频在线观看| 久久无码高潮喷水| 日韩av一区二区三区在线观看 | 日本黄色a视频| 欧美人与动牲交xxxxbbbb| 欧美精品xxxxbbbb| 精品国产91乱高清在线观看 | 欧美日韩精品在线| 最近中文字幕免费mv2018在线| 天堂中文av| 欧美精品91| 日韩成人动漫在线观看| 97人澡人人添人人爽欧美| 高清视频在线观看三级| 天堂在线一二区| 春暖花开亚洲| 欧美18xxxxx| 欧美国产精品v| 国产精品一区视频网站| 美女视频黄 久久| www.性欧美| 999色成人| 91精品二区| 视频一区视频二区在线观看| 亚洲一区二区三区四区不卡| 亚洲色图制服丝袜| 91精品国产高清一区二区三区| 日韩一级视频免费观看在线| 欧美美女网站色| 久久久综合激的五月天| 亚洲欧美综合另类在线卡通| ...xxx性欧美| 亚洲国产精品久久人人爱| 亚洲一区二区三区美女| 亚洲国产另类精品专区| 欧美日韩一区视频| 国产亚洲欧美一区| 国产伊人精品在线| 成人性做爰片免费视频| 国产精品无码专区av在线播放 | 李宗瑞系列合集久久| 午夜在线播放| 国产高清亚洲| 一本综合精品| 97久久精品人人做人人爽50路| 成人精品在线视频观看| 亚洲午夜电影在线观看| 欧美剧情片在线观看| 久久久久久久久爱| 久久久久久久电影一区| 国产99在线|中文| 91在线网站视频| 黄色影院一级片| 在线免费激情视频| 久久天天久久| 久久精品成人| 国产在线视频精品一区| 亚洲欧美国产三级| 亚洲欧洲午夜一线一品| 国产精品美女主播在线观看纯欲| 亚洲精品在线视频观看| 在线视频专区| 欧美亚洲国产日韩| 在线一区免费观看| 男女性色大片免费观看一区二区| 亚洲精品中文在线观看| 欧美日韩国产高清一区| 国产精品亚洲自拍| 日本丰满少妇xxxx| 成人在线app| **欧美日韩在线| 一本久道综合久久精品| 一色屋精品亚洲香蕉网站| 精品亚洲精品福利线在观看| 色就是色欧美| 一二三区在线视频|