您好,登錄后才能下訂單哦!
這篇文章主要介紹“Dubbo怎么實現擴展機制”,在日常操作中,相信很多人在Dubbo怎么實現擴展機制問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Dubbo怎么實現擴展機制”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
Dubbo 就是利用 SPI (Service Provider Interface)來實現擴展機制的。
這個 SPI 想必你們都很熟悉,在大學寫數據庫大作業的時候就碰到了,訪問數據庫需要用到 java.sql.Driver
。
市面上的數據庫五花八門,每個數據庫廠商都有自己的實現,所以肯定需要定制一個接口,這樣我們面向接口編程即可。
而具體的實現則可以通過配置來加載,JDK SPI 這時候就派上用場了。
其實一點都不神奇,就是約定一個地方,加載的時候就去那個地方找實現類。
約定一個地方直白點說就是代碼里面寫死了一個目錄,這個目錄就是 META-INF/services/
。
然后在這個目錄下創建一個文件,用接口全限定名來命名,文件內容就是實現類的全限定名。
到時候要實現類就根據接口名來這里找,然后實例化就行了。
挺簡單的吧,這就是 JDK SPI,但是它不滿足 Dubbo 的需求。
因為 Dubbo 把自身的一些實現也剝離出來成為擴展,而這些實現還是有點多的,也不需要全部用上。
如果用 JDK SPI 會把配置文件里面的類全部加載,這就導致資源的浪費。用的時候還需要遍歷過去才能找到對應的實現。
所以 Dubbo 就在 JDK SPI 的基礎上實現了個 Dubbo 的 SPI,可以根據指定的名稱按需加載實現類,比如拿 Cluster 來說就有這么多實現類。
約定的地方改了一下,一共有三個目錄。
META-INF/dubbo/internal/ :這里是存放 Dubbo 內部使用的 SPI 配置文件。
META-INF/dubbo/ :這里是存放用戶自定義 SPI 配置文件。
META-INF/services/:兼容 JDK SPI
然后文件里面的內容是key=value
形式,這樣就可以根據 key 找到對應的實現類。
然后在注解上可以配置默認的 key 來選擇默認的實現類,比如 Cluster 默認的實現是 failover。
也可以通過 URL 參數來選擇實現類。
還有像 JDK SPI 擴展點加載失敗的話,連擴展點名稱都拿不到,到時候報錯也不知道哪里出問題。
而 Dubbo SPI 則不會吃了錯誤,并且還提供了擴展點的自動注入和 AOP 功能。
大致了解了 Dubbo SPI 之后,我們再來深入看看實現細節。
Dubbo SPI 的核心實現在 ExtensionLoader 中,它負責擴展點的加載和生命周期的維護,類似 JDK SPI 的 ServiceLoader。
這里要先提一點看源碼的小技巧了。
開源框架都會有單元測試,而單元測試里面就會有我們看源碼時候想要的各種功能實現,我們就可以從單元測試入手得知一些功能的劃分,然后斷點調試逐漸深入。
比如今天文章的 ExtensionLoader ,它在 dubbo-common 模塊中,咱們就進入 test 來看看它測試用例怎么寫的。
當然除了通過文件夾來找,直接用文件名搜也行。
找到了就好辦了,數據都是造好的,找到你想要調試的方法,斷點一設,箭頭一點,這不就美滋滋了嗎?
好了,小技巧分享完畢,回到 ExtensionLoader,我們簡單點就用 Dubbo 單元測試的數據來看看實現。
有個叫 SimpleExt 的類,有三個實現,默認的實現是 impl1。
再來看看 SPI 配置文件的內容,可以看到為了測試還故意寫了一些空格在配置文件中。
然后現在如果要找 impl2 這個實現,通過以下代碼調用即可。
SimpleExt ext = ExtensionLoader .getExtensionLoader(SimpleExt.class).getExtension("impl2")
一個擴展接口對應有個 ExtensionLoader,找到對應的 ExtensionLoader,然后再加載對應名字的實現類。
接下來會有源碼,不過沒關系,還是很簡單的,想要深入源碼這關必須過。
可以看到getExtensionLoader
是靜態的,里面邏輯也很簡單就是從緩存找接口對應的 ExtensionLoader,找不到就新建一個返回。
現在有了 ExtensionLoader,咱們再來看看 getExtension 的邏輯,來看看是如何通過擴展點 name 找到對應的實現類的。
可以看到又是有個緩存操作,邏輯非常簡單,先去緩存找實例,如果沒有則創建實例。
要說細節就是用到了雙檢鎖,然后用 holder 來保證可見性和防止指令重排。應該看到注釋上的 holder 構造了吧,volatile 和雙檢鎖的搭配,這里就不深入了。
我們來看看 createExtension,這是要創建擴展點了,代碼有點長,但是我都做了相應的注釋,包括綠色的注釋。
邏輯還是很簡單的,詳細的代碼沒有具體展示,我先口述一下。
通過接口類名去三個目錄找到對應的文件。
解析文件內容生成 class 對象,然后緩存到 cachedClasses 中。
然后通過 name 去 cachedClasses 中找到對應的 class 對象。
去緩存 EXTENSION_INSTANCES 看看是否已經實例化過了。
沒有的話就實例化,然后調用 injectExtension 實現自動注入。
再通過 cachedWrapperClasses 實現包裝,將最后的包裝類返回。
有幾點不清晰沒關系,咱們接著分析,腦海中先大概知道要做什么,然后再來看看具體是怎么做的。
源碼中的 loadDirectory 就是去目錄找文件,然后解析,最終會調用 loadClass,這個方法很關鍵,我們詳細分析一下,為了便于觀看,刪除了一些代碼。
自適應咱們先略過,只要知道是在這里記錄的即可。
然后上面提到的 AOP 相關的 cachedWrapperClasses 就是在這里記錄的,如果判斷它是包裝類呢?
簡單粗暴但是有效,只要有當前類作為構造器參數的類就是包裝類,有點拗口,多讀幾遍就理解了。
現在我們再回過頭來看看這段代碼,Dubbo 的 AOP。
把擴展類對應的包裝類都記錄下來放在 cachedWrapperClasses 中,然后在實例化擴展類的時候就一層一層的把擴展類包起來,最終返回的就是包裝類。
為什么說這就是 AOP 呢?因為等于把一些邏輯切進了擴展實現類中。
其實就是把擴展對象的公共邏輯移到包裝類中,我們看下單元測試的例子就很清晰了。
從圖中可以看到有兩個擴展實現類,兩個包裝類,具體邏輯就不看了,不是重點,配置文件如下:
然后再看一下單元測試的運行結果,可以看到最終返回的其實是 Ext5Wrapper1 對象,并且它還包著 wrapper2 對象。
所以 echo 方法的調用鏈就是:Ext5Wrapper1 ->Ext5Wrapper2->Ext5impl1
也就起到了 AOP 的效果。
接下來我們再來看看 injectExtension,是如何實現 Dubbo 的自動注入。
到此,關于“Dubbo怎么實現擴展機制”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。