<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Dubbo的SPI機(jī)制是啥啊?

          共 4284字,需瀏覽 9分鐘

           ·

          2021-09-12 21:06



          前言

          之前大致的把Dubbo的運(yùn)作流程簡(jiǎn)單的分析了一遍了,Dubbo還有一個(gè)很大的優(yōu)點(diǎn),就是采用的微內(nèi)核+SPI擴(kuò)展設(shè)計(jì)

          這又是什么呢,這個(gè)可以很好的支持一些有特殊需求的三方的接入,可以自定義擴(kuò)展,自主定制二次開(kāi)發(fā),良好的擴(kuò)展性對(duì)于框架來(lái)說(shuō)是很重要的


          簡(jiǎn)單了解下SPI,全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機(jī)制。

          它通過(guò)在ClassPath路徑下的META-INF/services文件夾查找文件,自動(dòng)加載文件里所定義的類。這一機(jī)制為很多框架擴(kuò)展提供了可能,比如在Dubbo、JDBC中都使用到了SPI機(jī)制。

          舉個(gè)例子,比如你有個(gè)接口,現(xiàn)在這個(gè)接口有 3 個(gè)實(shí)現(xiàn)類,那么在系統(tǒng)運(yùn)行的時(shí)候?qū)@個(gè)接口到底選擇哪個(gè)實(shí)現(xiàn)類呢?這就需要SPI了,需要根據(jù)指定的配置或者是默認(rèn)的配置,去找到對(duì)應(yīng)的實(shí)現(xiàn)類加載進(jìn)來(lái),然后用這個(gè)實(shí)現(xiàn)類的實(shí)例對(duì)象。

          Java中JDK自身實(shí)現(xiàn)了SPI機(jī)制,基于策略模式來(lái)實(shí)現(xiàn)動(dòng)態(tài)加載的機(jī)制 。我們?cè)诔绦蛑欢x一個(gè)接口,具體的實(shí)現(xiàn)交個(gè)不同的服務(wù)提供者;在程序啟動(dòng)的時(shí)候,讀取配置文件,由配置確定要調(diào)用哪一個(gè)實(shí)現(xiàn)

          但是呢,存在一定的缺點(diǎn),比如不能按照需要加載,會(huì)一次性加載所有可用的擴(kuò)展點(diǎn),很多是不需要的,會(huì)浪費(fèi)系統(tǒng)資源;不支持AOP和依賴注入,實(shí)現(xiàn)類的方式也不夠靈活,只能通過(guò) Iterator 形式獲取

          你不夠強(qiáng),或者說(shuō)你做的不符合我的需求,我就替換你


          于是呢,dubbo重新實(shí)現(xiàn)了一套功能更強(qiáng)的 SPI 機(jī)制, 支持了AOP與依賴注入,并且 利用緩存提高加載實(shí)現(xiàn)類的性能,同時(shí)支持實(shí)現(xiàn)類的靈活獲取


          Java中的SPI

          Java中JDK自身實(shí)現(xiàn)了SPI機(jī)制,基于策略模式來(lái)實(shí)現(xiàn)動(dòng)態(tài)加載的機(jī)制 。我們?cè)诔绦蛑欢x一個(gè)接口,具體的實(shí)現(xiàn)交個(gè)不同的服務(wù)提供者;在程序啟動(dòng)的時(shí)候,讀取配置文件,由配置確定要調(diào)用哪一個(gè)實(shí)現(xiàn)

          首先,我們需要定義一個(gè)接口,SPIService

          public interface SPIService {    void execute();}
          然后,定義兩個(gè)實(shí)現(xiàn)類,沒(méi)別的意思,只輸入一句話。
          ?
          public class SpiImpl1 implements SPIService{    public void execute() {        System.out.println("SpiImpl1.execute()");    }}
          public class SpiImpl2 implements SPIService{ public void execute() { System.out.println("SpiImpl2.execute()"); }}
          最后呢,要在ClassPath路徑下配置添加一個(gè)文件。文件名字是接口的全限定類名,內(nèi)容是實(shí)現(xiàn)類的全限定類名,多個(gè)實(shí)現(xiàn)類用換行符分隔。內(nèi)容就是實(shí)現(xiàn)類的全限定類名:

          com.tech.dayu.spi.SpiImpl1com.tech.dayu.spi.SpiImpl2
          測(cè)試

          public class Test {    public static void main(String[] args) {            Iterator<SPIService> providers = Service.providers(SPIService.class);        ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class);
          while(providers.hasNext()) { SPIService ser = providers.next(); ser.execute(); } System.out.println("###################"); Iterator<SPIService> iterator = load.iterator(); while(iterator.hasNext()) { SPIService ser = iterator.next(); ser.execute(); } }}
          兩種方式的輸出結(jié)果是一致的:

          SpiImpl1.execute()SpiImpl2.execute()--------------------------------SpiImpl1.execute()SpiImpl2.execute()

          我們來(lái)看下源碼,位于java.util包下。我們就以ServiceLoader.load為例,通過(guò)源碼看看它里面到底怎么做的


          ServiceLoader.load()其實(shí)就是 Java SPI 入口



          看到最后調(diào)用的是reload,最后生效的是在這個(gè)LazyIterator的內(nèi)部,等同于是一個(gè)迭代器的遍歷,遍歷相應(yīng)的文件中的service的實(shí)現(xiàn)類,就是我們上面命名的那些


          這里無(wú)論是if還是else最后調(diào)用的都是nextService()方法,點(diǎn)進(jìn)去看


          可以看到無(wú)非就是通過(guò)名字獲取到文件路徑,獲取全限定名來(lái)加載類,并且創(chuàng)建其實(shí)例放入到相應(yīng)的緩存之后并且返回實(shí)例,這大體就是整個(gè)的實(shí)現(xiàn)邏輯,應(yīng)該不難吧,咱們自己來(lái)實(shí)現(xiàn)個(gè)這個(gè)應(yīng)該也是分分鐘的事

          好了,Java的SPI源碼分析的差不多了,問(wèn)題也隨之而來(lái),比如不能按照需要加載,會(huì)一次性加載所有可用的擴(kuò)展點(diǎn),很多是不需要的,會(huì)浪費(fèi)系統(tǒng)資源;不支持AOP和依賴注入,實(shí)現(xiàn)類的方式也不夠靈活,只能通過(guò) Iterator 形式獲取

          接下來(lái)咱們來(lái)分析Dubbo的SPI



          Dubbo中的SPI



          Dubbo 并未使用 Java SPI,而是重新實(shí)現(xiàn)了一套功能更強(qiáng)的 SPI 機(jī)制。

          Dubbo SPI 的相關(guān)邏輯被封裝在了 ExtensionLoader 類中,通過(guò) ExtensionLoader,我們可以加載指定的實(shí)現(xiàn)類。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路徑下。

          Dubbo要判斷一下,在系統(tǒng)運(yùn)行時(shí),應(yīng)該選用這個(gè)Protocol接口的哪個(gè)實(shí)現(xiàn)類。它會(huì)去找一個(gè)你配置的Protocol,將你配置的Protocol實(shí)現(xiàn)類,加載進(jìn)JVM,將其實(shí)例化,微內(nèi)核,可插拔,大量的組件,Protocol負(fù)責(zé)RPC調(diào)用的東西,你可以實(shí)現(xiàn)自己的RPC調(diào)用組件,實(shí)現(xiàn)Protocol接口,給自己的一個(gè)實(shí)現(xiàn)類就可以啦

          Dubbo里很多都是保留一個(gè)接口和多個(gè)實(shí)現(xiàn),然后在系統(tǒng)運(yùn)行的時(shí)候動(dòng)態(tài)根據(jù)配置去找到對(duì)應(yīng)的實(shí)現(xiàn)類。如果你沒(méi)配置,那就走默認(rèn)的實(shí)現(xiàn)就可以啦

          我們隨便來(lái)看一下其中的


          并且 Dubbo SPI 除了可以按需加載實(shí)現(xiàn)類之外,增加了 IOC 和 AOP 的特性,還有個(gè)自適應(yīng)擴(kuò)展機(jī)制。

          我們先來(lái)看一下 Dubbo 對(duì)配置文件目錄的約定,不同于 Java SPI ,Dubbo 分為了三類目錄。

          • META-INF/services/ 目錄:該目錄下的 SPI 配置文件是為了用來(lái)兼容 Java SPI 。

          • META-INF/dubbo/ 目錄:該目錄存放用戶自定義的 SPI 配置文件。

          • META-INF/dubbo/internal/ 目錄:該目錄存放 Dubbo 內(nèi)部使用的 SPI 配置文件。


          接下來(lái)我們來(lái)看Dubbo的SPI的源碼

          在Dubbo中ExtensionLoader類似 Java SPI 中 ServiceLoader 的存在。大致流程就是先通過(guò)接口類找到ExtensionLoader ,然后再通過(guò) ExtensionLoader.getExtension(name) 得到指定名字的實(shí)現(xiàn)類實(shí)例。


          其實(shí)也是很簡(jiǎn)單的,就是通過(guò)一頓判斷然后在緩存中檢查是否存在這個(gè)類型的ExtensionLoader ,沒(méi)有的話就新建一個(gè)放進(jìn)去緩存,最后返回接口類的對(duì)應(yīng)的ExtensionLoader

          getExtension() 方法,從現(xiàn)象我們可以知道這個(gè)方法就是從類對(duì)應(yīng)的 ExtensionLoader 中通過(guò)名字找到實(shí)例化完的實(shí)現(xiàn)類



          接下來(lái)我們來(lái)看Dubbo的SPI的源碼

          內(nèi)部的createExtension()方法,我就不截圖了,比較長(zhǎng),就是先找實(shí)現(xiàn)類,判斷是否有該類的緩存,沒(méi)有的話就通過(guò)反射新建一個(gè)實(shí)例對(duì)象,然后放進(jìn)去

          到這里其實(shí)就差不多了分析的,拿到實(shí)例對(duì)然后就可以執(zhí)行了

          Dubbo的SPI主要就是為了增加框架的可拓展性,可以在其基礎(chǔ)上進(jìn)行二次開(kāi)發(fā),還有一個(gè)更重要的點(diǎn)就是不會(huì)像Java的SPI一樣直接全部加載,那樣可能會(huì)造成大量的資源浪費(fèi)的,甚至可能還會(huì)做無(wú)用功



          結(jié)尾


          好了,以上就是全部?jī)?nèi)容了,我是小魚仙,你們的學(xué)習(xí)成長(zhǎng)小伙伴         

                                                                    

          我希望有一天能夠靠寫字養(yǎng)活自己,現(xiàn)在還在磨練,這個(gè)時(shí)間可能會(huì)有很多年,感謝你們做我最初的讀者和傳播者。請(qǐng)大家相信,只要給我一份愛(ài),我終究會(huì)還你們一頁(yè)情的。

          再次感謝大家能夠讀到這里,我后面會(huì)持續(xù)的更新技術(shù)文章以及一些記錄生活的靈魂文章,如果覺(jué)得不錯(cuò)的,覺(jué)得大魚同學(xué)有點(diǎn)東西的話,求點(diǎn)贊、關(guān)注、分享三連

          哦,對(duì)了!后續(xù)的更新文章我都會(huì)及時(shí)放到這里,歡迎大家點(diǎn)擊觀看,都是干貨文章啊,建議收藏,以后隨時(shí)翻閱查看

          https://github.com/DayuMM2021/Java


          瀏覽 89
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  好看日逼小视频 | 精品欧美乱码久久久久久 | 亚洲综合五月天婷婷丁香2 | 超碰中文字幕在线观看 | 免费黄色小说网站 |