色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Java、Spring、Dubbo三者SPI機制的原理和區別

jf_ro2CN3Fa ? 來源:三友的java日記 ? 2023-06-05 15:21 ? 次閱讀

今天來跟大家聊一聊Java、Spring、Dubbo三者SPI機制的原理和區別。

其實我之前寫過一篇類似的文章,但是這篇文章主要是剖析dubbo的SPI機制的源碼,中間只是簡單地介紹了一下Java、Spring的SPI機制,并沒有進行深入,所以本篇就來深入聊一聊這三者的原理和區別。

什么是SPI

SPI全稱為Service Provider Interface,是一種動態替換發現的機制,一種解耦非常優秀的思想,SPI可以很靈活的讓接口和實現分離,讓api提供者只提供接口,第三方來實現,然后可以使用配置文件的方式來實現替換或者擴展,在框架中比較常見,提高框架的可擴展性。

簡單來說SPI是一種非常優秀的設計思想,它的核心就是解耦、方便擴展。

基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

項目地址:https://github.com/YunaiV/ruoyi-vue-pro

視頻教程:https://doc.iocoder.cn/video/

Java SPI機制--ServiceLoader

ServiceLoader是Java提供的一種簡單的SPI機制的實現,Java的SPI實現約定了以下兩件事:

文件必須放在META-INF/services/目錄底下

文件名必須為接口的全限定名,內容為接口實現的全限定名

這樣就能夠通過ServiceLoader加載到文件中接口的實現。

來個demo

第一步,需要一個接口以及他的實現類

publicinterfaceLoadBalance{
}

publicclassRandomLoadBalanceimplementsLoadBalance{
}

第二步,在META-INF/services/目錄創建一個文件名LoadBalance全限定名的文件,文件內容為RandomLoadBalance的全限定名

36482d32-02a3-11ee-90ce-dac502259ad0.png36621418-02a3-11ee-90ce-dac502259ad0.png

測試類:

publicclassServiceLoaderDemo{

publicstaticvoidmain(String[]args){
ServiceLoaderloadBalanceServiceLoader=ServiceLoader.load(LoadBalance.class);
Iteratoriterator=loadBalanceServiceLoader.iterator();
while(iterator.hasNext()){
LoadBalanceloadBalance=iterator.next();
System.out.println("獲取到負載均衡策略:"+loadBalance);
}
}

}

測試結果:

36814fcc-02a3-11ee-90ce-dac502259ad0.png

此時就成功獲取到了實現。

在實際的框架設計中,上面這段測試代碼其實是框架作者寫到框架內部的,而對于框架的使用者來說,要想自定義LoadBalance實現,嵌入到框架,僅僅只需要寫接口的實現和spi文件即可。

實現原理

如下是ServiceLoader中一段核心代碼

36b0081c-02a3-11ee-90ce-dac502259ad0.png

首先獲取一個fullName,其實就是META-INF/services/接口的全限定名

36f39ff0-02a3-11ee-90ce-dac502259ad0.png

然后通過ClassLoader獲取到資源,其實就是接口的全限定名文件對應的資源,然后交給parse方法解析資源

3725e2d0-02a3-11ee-90ce-dac502259ad0.png

parse方法其實就是通過IO流讀取文件的內容,這樣就可以獲取到接口的實現的全限定名

再后面其實就是通過反射實例化對象,這里就不展示了。

所以其實不難發現ServiceLoader實現原理比較簡單,總結起來就是通過IO流讀取META-INF/services/接口的全限定名文件的內容,然后反射實例化對象。

優缺點

由于Java的SPI機制實現的比較簡單,所以他也有一些缺點。

第一點就是浪費資源,雖然例子中只有一個實現類,但是實際情況下可能會有很多實現類,而Java的SPI會一股腦全進行實例化,但是這些實現了不一定都用得著,所以就會白白浪費資源。

第二點就是無法對區分具體的實現,也就是這么多實現類,到底該用哪個實現呢?如果要判斷具體使用哪個,只能依靠接口本身的設計,比如接口可以設計為一個策略接口,又或者接口可以設計帶有優先級的,但是不論怎樣設計,框架作者都得寫代碼進行判斷。

所以總得來說就是ServiceLoader無法做到按需加載或者按需獲取某個具體的實現。

使用場景

雖然說ServiceLoader可能有些缺點,但是還是有使用場景的,比如說:

不需要選擇具體的實現,每個被加載的實現都需要被用到

雖然需要選擇具體的實現,但是可以通過對接口的設計來解決

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

項目地址:https://github.com/YunaiV/yudao-cloud

視頻教程:https://doc.iocoder.cn/video/

Spring SPI機制--SpringFactoriesLoader

Spring我們都不陌生,他也提供了一種SPI的實現SpringFactoriesLoader。

Spring的SPI機制的約定如下:

配置文件必須在META-INF/目錄下,文件名必須為spring.factories

文件內容為鍵值對,一個鍵可以有多個值,只需要用逗號分割就行,同時鍵值都需要是類的全限定名,鍵和值可以沒有任何類與類之間的關系,當然也可以有實現的關系。

所以也可以看出,Spring的SPI機制跟Java的不論是文件名還是內容約定都不一樣。

來個demo

在META-INF/目錄下創建spring.factories文件,LoadBalance為鍵,RandomLoadBalance為值

375b7526-02a3-11ee-90ce-dac502259ad0.png3778872e-02a3-11ee-90ce-dac502259ad0.png

測試:

publicclassSpringFactoriesLoaderDemo{

publicstaticvoidmain(String[]args){
ListloadBalances=SpringFactoriesLoader.loadFactories(LoadBalance.class,MyEnableAutoConfiguration.class.getClassLoader());
for(LoadBalanceloadBalance:loadBalances){
System.out.println("獲取到LoadBalance對象:"+loadBalance);
}
}

}

運行結果:

379c26b6-02a3-11ee-90ce-dac502259ad0.png

成功獲取到了實現對象。

核心原理

如下是SpringFactoriesLoader中一段核心代碼

37c72f8c-02a3-11ee-90ce-dac502259ad0.png

其實從這可以看出,跟Java實現的差不多,只不過讀的是META-INF/目錄下spring.factories文件內容,然后解析出來鍵值對。

使用場景

Spring的SPI機制在內部使用的非常多,尤其在SpringBoot中大量使用,SpringBoot啟動過程中很多擴展點都是通過SPI機制來實現的,這里我舉兩個例子

1、自動裝配

在SpringBoot3.0之前的版本,自動裝配是通過SpringFactoriesLoader來加載的。

37f9d3f6-02a3-11ee-90ce-dac502259ad0.png

但是SpringBoot3.0之后不再使用SpringFactoriesLoader,而是Spring重新從META-INF/spring/目錄下的org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中讀取了。

38366dfc-02a3-11ee-90ce-dac502259ad0.png

至于如何讀取的,其實猜也能猜到就跟上面SPI機制讀取的方式大概差不多,就是文件路徑和名稱不一樣。

2、PropertySourceLoader的加載

PropertySourceLoader是用來解析application配置文件的,它是一個接口

38716c18-02a3-11ee-90ce-dac502259ad0.png

SpringBoot默認提供了 PropertiesPropertySourceLoader 和 YamlPropertySourceLoader兩個實現,就是對應properties和yaml文件格式的解析。

SpringBoot在加載PropertySourceLoader時就用了SPI機制

38a7afc6-02a3-11ee-90ce-dac502259ad0.png

與Java SPI機制對比

首先Spring的SPI機制對Java的SPI機制對進行了一些簡化,Java的SPI每個接口都需要對應的文件,而Spring的SPI機制只需要一個spring.factories文件。

其次是內容,Java的SPI機制文件內容必須為接口的實現類,而Spring的SPI并不要求鍵值對必須有什么關系,更加靈活。

第三點就是Spring的SPI機制提供了獲取類限定名的方法loadFactoryNames,而Java的SPI機制是沒有的。通過這個方法獲取到類限定名之后就可以將這些類注入到Spring容器中,用Spring容器加載這些Bean,而不僅僅是通過反射。

但是Spring的SPI也同樣沒有實現獲取指定某個指定實現類的功能,所以要想能夠找到具體的某個實現類,還得依靠具體接口的設計。

所以不知道你有沒有發現,PropertySourceLoader它其實就是一個策略接口,注釋也有說,所以當你的配置文件是properties格式的時候,他可以找到解析properties格式的PropertiesPropertySourceLoader對象來解析配置文件。

Dubbo SPI機制--ExtensionLoader

ExtensionLoader是dubbo的SPI機制的實現類。每一個接口都會有一個自己的ExtensionLoader實例對象,這點跟Java的SPI機制是一樣的。

同樣地,Dubbo的SPI機制也做了以下幾點約定:

接口必須要加@SPI注解

配置文件可以放在META-INF/services/、META-INF/dubbo/internal/ 、META-INF/dubbo/ 、META-INF/dubbo/external/這四個目錄底下,文件名也是接口的全限定名

內容為鍵值對,鍵為短名稱(可以理解為spring中Bean的名稱),值為實現類的全限定名

先來個demo

首先在LoadBalance接口上@SPI注解

@SPI
publicinterfaceLoadBalance{

}

然后,修改一下Java的SPI機制測試時配置文件內容,改為鍵值對,因為Dubbo的SPI機制也可以從META-INF/services/目錄下讀取文件,所以這里就沒重寫文件

random=com.sanyou.spi.demo.RandomLoadBalance

測試類:

publicclassExtensionLoaderDemo{

publicstaticvoidmain(String[]args){
ExtensionLoaderextensionLoader=ExtensionLoader.getExtensionLoader(LoadBalance.class);
LoadBalanceloadBalance=extensionLoader.getExtension("random");
System.out.println("獲取到random鍵對應的實現類對象:"+loadBalance);
}

}

通過ExtensionLoader的getExtension方法,傳入短名稱,這樣就可以精確地找到短名稱對的實現類。

所以從這可以看出Dubbo的SPI機制解決了前面提到的無法獲取指定實現類的問題。

測試結果:

38d96566-02a3-11ee-90ce-dac502259ad0.png

dubbo的SPI機制除了解決了無法獲取指定實現類的問題,還提供了很多額外的功能,這些功能在dubbo內部用的非常多,接下來就來詳細講講。

dubbo核心機制

1、自適應機制

自適應,自適應擴展類的含義是說,基于參數,在運行時動態選擇到具體的目標類,然后執行。

每個接口有且只能有一個自適應類,通過ExtensionLoader的getAdaptiveExtension方法就可以獲取到這個類的對象,這個對象可以根據運行時具體的參數找到目標實現類對象,然后調用目標對象的方法。

舉個例子,假設上面的LoadBalance有個自適應對象,那么獲取到這個自適應對象之后,如果在運行期間傳入了random這個key,那么這個自適應對象就會找到random這個key對應的實現類,調用那個實現類的方法,如果動態傳入了其它的key,就路由到其它的實現類。

自適應類有兩種方式產生,第一種就是自己指定,在接口的實現類上加@Adaptive注解,那么這個這個實現類就是自適應實現類。

@Adaptive
publicclassRandomLoadBalanceimplementsLoadBalance{
}

除了自己代碼指定,還有一種就是dubbo會根據一些條件幫你動態生成一個自適應類,生成過程比較復雜,這里就不展開了。

自適應機制在Dubbo中用的非常多,而且很多都是自動生成的,如果你不知道Dubbo的自適應機制,你在讀源碼的時候可能都不知道為什么代碼可以走到那里。。

2、IOC和AOP

一提到IOC和AOP,立馬想到的都是Spring,但是IOC和AOP并不是Spring特有的概念,Dubbo也實現IOC和AOP的功能,但是是一個輕量級的。

2.1、依賴注入

Dubbo依賴注入是通過setter注入的方式,注入的對象默認就是上面提到的自適應的對象,在Spring環境下可以注入Spring Bean。

publicclassRoundRobinLoadBalanceimplementsLoadBalance{

privateLoadBalanceloadBalance;

publicvoidsetLoadBalance(LoadBalanceloadBalance){
this.loadBalance=loadBalance;
}

}

如上代碼,RoundRobinLoadBalance中有一個setLoadBalance方法,參數LoadBalance,在創建RoundRobinLoadBalance的時候,在非Spring環境底下,Dubbo就會找到LoadBalance自適應對象然后通過反射注入。

這種方式在Dubbo中也很常見,比如如下的一個場景

39029850-02a3-11ee-90ce-dac502259ad0.png

RegistryProtocol中會注入一個Protocol,其實這個注入的Protocol就是一個自適應對象。

2.2、接口回調

Dubbo也提供了一些類似于Spring的一些接口的回調功能,比如說,如果你的類實現了Lifecycle接口,那么創建或者銷毀的時候就會回調以下幾個方法

39245b0c-02a3-11ee-90ce-dac502259ad0.png

在dubbo3.x的某個版本之后,dubbo提供了更多接口回調,比如說ExtensionPostProcessor、ExtensionAccessorAware,命名跟Spring的非常相似,作用也差不多。

2.3、自動包裝

自動包裝其實就是aop的功能實現,對目標對象進行代理,并且這個aop功能在默認情況下就是開啟的。

在Dubbo中SPI接口的實現中,有一種特殊的類,被稱為Wrapper類,這個類的作用就是來實現AOP的。

判斷Wrapper類的唯一標準就是這個類中必須要有這么一個構造參數,這個構造方法的參數只有一個,并且參數類型就是接口的類型,如下代碼:

publicclassRoundRobinLoadBalanceimplementsLoadBalance{

privatefinalLoadBalanceloadBalance;

publicRoundRobinLoadBalance(LoadBalanceloadBalance){
this.loadBalance=loadBalance;
}

}

此時RoundRobinLoadBalance就是一個Wrapper類。

當通過random獲取RandomLoadBalance目標對象時,那么默認情況下就會對RandomLoadBalance進行包裝,真正獲取到的其實是RoundRobinLoadBalance對象,RoundRobinLoadBalance內部引用的對象是RandomLoadBalance。

*測試一下 *

在配置文件中加入

roundrobin=com.sanyou.spi.demo.RoundRobinLoadBalance

測試結果

395360be-02a3-11ee-90ce-dac502259ad0.png

從結果可以看出,雖然指定了random,但是實際獲取到的是RoundRobinLoadBalance,而RoundRobinLoadBalance內部引用了RandomLoadBalance。

如果有很多的包裝類,那么就會形成一個責任鏈條,一個套一個。

所以dubbo的aop跟spring的aop實現是不一樣的,spring的aop底層是基于動態代理來的,而dubbo的aop其實算是靜態代理,dubbo會幫你自動組裝這個代理,形成一條責任鏈。

到這其實我們已經知道,dubbo的spi接口的實現類已經有兩種類型了:

自適應類

Wrapper類

除了這兩種類型,其實還有一種,叫做默認類,就是@SPI注解的值對應的實現類,比如

@SPI("random")
publicinterfaceLoadBalance{

}

此時random這個key對應的實現類就是默認實現,通過getDefaultExtension這個方法就可以獲取到默認實現對象。

3、自動激活

所謂的自動激活,就是根據你的入參,動態地選擇一批實現類返回給你。

自動激活的實現類上需要加上Activate注解,這里就又學習了一種實現類的分類。

@Activate
publicinterfaceRandomLoadBalance{

}

此時RandomLoadBalance就屬于可以被自動激活的類。

獲取自動激活類的方法是getActivateExtension,所以根據這個方法的入參,可以動態選擇一批實現類。

自動激活這個機制在Dubbo一個核心的使用場景就是Filter過濾器鏈中。

Filter是dubbo中的一個擴展點,可以在請求發起前或者是響應獲取之后就行攔截,作用有點像Spring MVC中的HandlerInterceptor。

39973640-02a3-11ee-90ce-dac502259ad0.pngFilter的一些實現類

如上Filter有很多實現,所以為了能夠區分Filter的實現是作用于provider的還是consumer端,所以就可以用自動激活的機制來根據入參來動態選擇一批Filter實現。

比如說ConsumerContextFilter這個Filter就作用于Consumer端。

39c4e018-02a3-11ee-90ce-dac502259ad0.pngConsumerContextFilter

總結

通過以上分析可以看出,實現SPI機制的核心原理就是通過IO流讀取指定文件的內容,然后解析,最后加入一些自己的特性。

最后總的來說,Java的SPI實現的比較簡單,并沒有什么其它功能;Spring得益于自身的ioc和aop的功能,所以也沒有實現太復雜的SPI機制,僅僅是對Java做了一點簡化和優化;但是dubbo的SPI機制為了滿足自身框架的使用要求,實現的功能就比較多,不僅將ioc和aop的功能集成到SPI機制中,還提供注入自適應和自動激活等功能。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    19

    文章

    2969

    瀏覽量

    104780
  • SPI
    SPI
    +關注

    關注

    17

    文章

    1707

    瀏覽量

    91627
  • 源碼
    +關注

    關注

    8

    文章

    642

    瀏覽量

    29226
  • spring
    +關注

    關注

    0

    文章

    340

    瀏覽量

    14346
  • Dubbo
    +關注

    關注

    0

    文章

    20

    瀏覽量

    3183

原文標題:聊聊SPI

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    java spring教程

    java spring教程理解Spring 實現原理掌握Spring IOC,AOP掌握Spring的基礎配置和用法熟練使用SSH開發項目
    發表于 09-11 11:09

    什么是java spring

    什么是java springSpring是一個開源框架,它由Rod Johnson創建。它是為了解決企業應用開發的復雜性而創建的。Spring使用基本的JavaBean來完成以前只可能由EJB完成
    發表于 09-11 11:16

    聊聊Dubbo - Dubbo可擴展機制實戰

    OSGI容器Dubbo作為一個框架,不希望強依賴其他的IoC容器,比如Spring,Guice。OSGI也是一個很重的實現,不適合Dubbo。最終Dubbo的實現參考了
    發表于 06-04 17:33

    聊聊Dubbo - Dubbo可擴展機制源碼解析

    的場景中,類之間都是有依賴的。擴展實例中也會引用一些依賴,比如簡單的Java類,另一個Dubbo的擴展或一個Spring Bean等。依賴的情況很復雜,Dubbo的處理也相對復雜些。我
    發表于 06-05 18:43

    Dubbo開源現狀與未來規劃

    考慮到有些同學可能對 Dubbo 不太熟悉,我先簡單介紹下 Dubbo 的工作原理和架構。簡單的說,Dubbo 是 基于 Java 的 RPC 框架。
    發表于 07-05 15:21

    Dubbo Cloud Native 之路的實踐與思考

    Jaeger,三者的靈感均來自于 Google 論文 Dapper。相對而言,Java 程序員可能更為熟悉 Zipkin,因為它是 Spring Cloud Sleuth 首選方案,提供客戶端
    發表于 07-05 16:05

    請問xdata和bdata和普通變量三者有什么區別

    如題,請問xdata和bdata和普通變量三者區別
    發表于 09-11 04:35

    UART SPI IIC的詳解及三者區別和聯系

    詳情參考文章01詳情參考文章02UART SPI IIC的詳解及三者區別和聯系Arduino主從機之間的i2c通信I2C總線定義:I2C(‘intel’ -Integrated Circuit
    發表于 12-13 07:27

    USART,串口和USB這三者區別

    參考文獻:區分:串口,COM口,UART,USART,串口和USB這三者區別1.串口 COM UART JTAG(它們是一個類別的,對應的是硬件,區別是設備...
    發表于 12-16 06:51

    ADISimPE和ADISimRF,ADISimPLL三者有什么區別

    為什么在ADIsimPE中找不到ADL系列的仿真模型,比如ADL5535,。 而且請問ADISimPE和ADISimRF,ADISimPLL三者有什么區別
    發表于 11-17 11:32

    Dubbo源代碼實現服務調用的動態代理和負載均衡

    。現在,我們從源碼的角度來看看,Dubbo是如何做到這點的。我們知道,要成為Dubbo服務的消費,需要在Spring的xml文件中配置
    發表于 03-12 14:35 ?0次下載

    介紹PWM、 PPM、S-BUS這三者區別

    介紹PWM、PPM、S-BUS這三者區別
    的頭像 發表于 03-08 11:32 ?8081次閱讀

    JDK內置的一種服務SPI機制

    SPI(Service Provider Interface)是JDK內置的一種服務提供發現機制,可以用來啟用框架擴展和替換組件,主要用于框架中開發,例如DubboSpring
    的頭像 發表于 02-15 09:15 ?799次閱讀

    基于springSPI擴展機制是如何實現的?

    基本上,你一說是基于 springSPI 擴展機制,再把spring.factories文件和EnableAutoConfiguration提一下,那么這個問題就答的八九不離十了
    的頭像 發表于 03-07 09:17 ?1055次閱讀

    dubbospring cloud區別

    DubboSpring Cloud是兩個非常流行的微服務框架,各有自己的特點和優勢。在本文中,我們將詳細介紹DubboSpring Cloud的
    的頭像 發表于 12-04 14:47 ?1683次閱讀
    主站蜘蛛池模板: 俄罗斯1819y0u| 动漫美女被h动态图| CHESENGAY痞帅警察GV| 久久精品国产亚洲AV影院| 小小水蜜桃3视频在线观看| 纯肉腐文高H总受男男| 琪琪色在线播放| 99热热在线精品久久| 老太婆性BBWBBW| 66美女人体| 免费精品国偷自产在线在线| 最近中文字幕在线中文高清版 | 国产极品美女视频福利| 日韩人妻无码专区一本二本| 大地影院在线播放| 少妇精油按摩| 国产日韩久久久精品影院首页| 我和妽妽在厨房里的激情区二区 | 久久综合香蕉久久久久久久| 中文有码中文字幕免费视频| 伦理 电影在线观看| 99视频免费看| 日韩人妻无码精品久久中文字幕| 国产成人教育视频在线观看| 亚欧乱亚欧乱色视频| 久久88综合| 99精品国产AV一区二区麻豆| 欧美一级久久久久久久大| 岛国大片在线播放高清| 小雪奶水涨翁工帮吸的推荐语录 | 最近中文字幕免费高清MV视频6| 榴莲推广APP网站入口下载安装| 99国产在线视频有精品视频| 任你懆视频 这里只有精品| 国产成人在线免费观看| 亚洲精品国偷拍自产在线| 久久怡红院国产精品| 超碰在线视频| 亚洲天堂2017无码| 欧美性视频xxxxhd| 国产欧美国日产在线播放|