歡迎光臨
每天分享高質量文章

SOFABoot 擴充套件點初體驗 | SOFALab 實踐系列

SOFABoot 是基於 Spring Boot 的一套研發框架。

 

在完全相容 Spring Boot 的基礎上,SOFABoot 還提供了啟動期監控檢查,背景關係隔離,模組化開發,類隔離,日誌空間隔離等等能力。

 

SOFABoot 地址:

https://github.com/alipay/sofa-boot

 

本文工程案例:

https://github.com/glmapper/glmapper-sofa-extension

模組化與擴充套件點

春節前 SOFABoot 釋出了 2.6.x 系列版本,新特性相當給力,不知道是否引起了你的關註?這裡簡單羅列下新特性:

  • 支援擴充套件和擴充套件點

  • 在掃清背景關係期間支援 spring bean 的並行初始化

  • 支援使用註解方式釋出 JVM 服務

之前的文章中有 @玄北 寫過的模組化的文章( 傳送門 : 剖析 | 詳談 SOFABoot 模組化原理  & 實操 | 基於 SOFABoot 進行模組化開發 ),這兩篇文章中介紹了模組化的幾種實現方案(PS:當然主要還是為了宣傳一下 SOFABoot 提供的基於 Spring 背景關係隔離的模組化能力)。SOFABoot 的模組隔離方案是為瞭解決傳統的模組化方案模組化不徹底的問題,從 2.4.0 版本開始支援基於 Spring 背景關係隔離的模組化能力,每個 SOFABoot 模組使用獨立的 Spring 背景關係,每個模組自包含,模組與模組之間透過 JVM Service 進行通訊,從而避免模組間的緊耦合。

在 Spring 背景關係隔離的情況下,各個背景關係之間的 bean 是互不可見;SOFABoot 中透過釋出 JVM 服務的方式使得不同模組 bean 之間的訪問得以實現。但是同時又帶來了另外一個問題,如果一個模組以獨立 jar 的方式對外提供 api ,那麼對於其他依賴此模組的模組來說,就無法去改變這個模組中的 bean 實體行為。

在實際的使用場景中,一個模組中的 bean 有時候需要開放一些入口,供另外一個模組擴充套件。SOFABoot 借鑒和使用了 Nuxeo Runtime 專案 以及 nuxeo 專案,併在上面擴充套件,與 Spring 融合,提供了擴充套件點的能力。

本篇將針對 SOFABoot 2.6.x 版本中提供的擴充套件點進行簡單嘗試,結合官方檔案提供的示例,一步一步實現我們自定義的一個擴充套件點功能(本文過於簡單,可能會引起極度舒適,請備好被子和熱水袋)。

案例背景

這裡先丟擲一個例子,現在有一個三方 jar ,它定義了獲取資料源介面的頂層抽象;不同的業務方如果依賴這個 jar,則需要自己提供一個資料源的實現,當然其本身提供了預設實現(假設是 mysql)。基於此我們大概能夠想到的方式就是基於 SPI 來提供這種擴充套件能力,但是對於在 Spring 背景關係隔離的情況下,業務方的 Spring 背景關係是無法與引入 jar 的背景關係共享 bean 的,這樣自然也就無法實現對原有資料源實現的擴充套件。

那麼這裡我們就可以選擇使用 SOFABoot 擴充套件點來實現,這裡有兩個比較重要的概念,也是擴充套件點區別於 SPI 的主要特性:

  • 可以在基於 Spring 背景關係隔離的情況下實現擴充套件

  • 擴充套件的是 Spring Bean 

下麵基於這兩個點,來完成自定義擴充套件點的一個案例。在實現上述案例之前我們需要先構建一個基於 Spring 背景關係隔離的模組化工程,然後再簡單介紹下擴充套件點的基本使用方式。

構建模組化工程

SOFABoot 開源版本中並沒有給出擴充套件點相關的案例工程,只是在測試用例中進行了詳細的測試,有興趣的同學可以看下相關測試用例程式碼。實際上測試用例中也沒有涉及到在模組化的場景下使用擴充套件點,測試用例都是基於同一個Spring 背景關係來完成的。本篇文章將先搭建一個簡單的模組化工程,然後基於此工程來實現擴充套件點的能力。

本工程主要包括 4 個模組:

  • glmapper-sofa-facade         // JVM 服務釋出與取用的 API 包

  • glmapper-sofa-provider      // Annotation 方式釋出 JVM 服務

  • glmapper-sofa-consumer    // Annotation 方式取用 JVM 服務

  • glmapper-sofa-web             // 啟動包含 SOFABoot 模組的 SOFA Boot 應用

官方檔案及案例 中給的比較複雜,包含了多種使用服務釋出和取用的方式,這裡我使用了最新提供的基於註解的方式來實現;獲取本文工程案例:

https://github.com/glmapper/glmapper-sofa-extension

擴充套件點基本使用

在 SOFABoot 中使用擴充套件點能力,需要以下三個步驟:

  • 定義提供擴充套件能力的 bean

  • 定義擴充套件點

  • 定義擴充套件並使用

這三步中前兩步都是由服務提供方來完成,最後一步由具體的業務使用方式來定義。

1、定義提供擴充套件能力的 bean

本案例工程中,是將 glmapper-sofa-provider 作為服務提供方,因此也在此模組下定義一個具有擴充套件能力的 bean 。

定義這個介面的實現:

在模組的 Spring 配置檔案 

resources/META-INF/service-provider.xml  

中,我們把這個 bean 給配置起來:

2、定義擴充套件點

在上面的 bean 中有一個欄位 word ,在實際中,我們希望這個欄位能夠被其他的模組自定義進行改寫,這裡我們將其以擴充套件點的形式暴露出來。這裡先定義一個類去描述這個擴充套件點:

然後在模組的 Spring 配置檔案 

resources/META-INF/service-provider.xml  

中定義擴充套件點:

  • name   :為擴充套件點的名字

  • ref       :為擴充套件點所作用在的 bean

  • object :為擴充套件點的貢獻點具體的描述,這個描述是透過 XMap 的方式來進行的(XMap 的作用是將 Java 物件和 XML 檔案進行對映,這裡建議透過在網上搜索下 XMap 的檔案來瞭解 XMap)

至此服務提供端已經暴露出了擴充套件點,那麼在服務使用端,也就是需要擴充套件這個 bean 的使用方就可以擴充套件這個bean 了。

3、定義擴充套件

上述已經將擴充套件點定義好了,此時我們就可以對這個 bean 進行擴充套件了。擴充套件是具體業務使用方來做的事,在本案例中,glmapper-sofa-web 模組作為使用服務使用方,因此在 resources/META-INF/spring/web-home.xml 下進行擴充套件定義:

  • bean     :為擴充套件所作用在的 bean

  • point    :為擴充套件點的名字

  • content  裡面的內容為擴充套件的定義,會透過 XMap 將內容解析為:擴充套件點的貢獻點具體的描述物件,在這裡即為 com.glmapper.bridge.extension.ExtensionDescriptor 物件

需要註意一點,glmapper-sofa-web 模組不是一個 SOFABoot 模組,這裡留坑。

編寫一個 TestController 類,這裡最先參考的是 SOFABoot 測試用例中的寫法,如下:

啟動執行,結果拋了一個錯:

沒有找到 extension 這個 bean ,但是實際上我們在前面 *定義提供擴充套件能力的 bean *小結中已經將 extension 配置成一個 bean 了。

原因在於,glmapper-sofa-provider 是一個 SOFABoot 模組,它有自己獨立的 Spring 背景關係環境,web 模組這裡作為根背景關係無法感知到這個 bean 的存在,所以這裡還需要將 extension 這個釋出成一個 JVM 服務,然後才能正常啟動。具體就是在 IExtensionImpl 類上加上 @SofaService 註解。然後在 TestController 中,將@Autowired 改成 @SofaReference 。

另外,因為 glmapper-sofa-web 不是一個 SOFABoot 模組(這裡特指的是 isle 模組),在  resources/META-INF/spring/web-home.xml 定義的擴充套件無法直接被 spring 掃到,因此還要在啟動類上使用 @ImportResource 來指定當前 web 模組的 xml 檔案位置,否則工程可以正常執行,但是基於此工程擴充套件點擴充套件的能力是無效的。

4、registerExtension 

細心的同學可以註意到了一個點,就是前面擴充套件點實現 IExtensionImpl 這個類中有一個特殊的方法,在整個案例演示中其實都是沒有用到的。

最開始對這個方法我也很迷糊,因為我並沒有用到。既然自己沒用到,那一定是框架自己用到了。有興趣的同學可以自己斷點下這段邏輯。實際上 SOFABoot 在解析到貢獻點時,會呼叫被擴充套件 bean 的 registerExtension 方法,其中包含了使用者定義的貢獻點處理邏輯。在上述的例子中,獲取使用者定義的 value 值,並將其設定到 word 欄位中改寫 bean 中原始定義的值,最後呼叫 extension.say() 方法,可以看到傳回擴充套件中定義的值: newValue 。

XMap 支援和擴充套件

上述的例子中只是一個很簡單的擴充套件,其實 XMap 包含了非常豐富的描述能力,包括 List, Map 等,這些可以透過檢視 XMap 的檔案 來瞭解。在 SOFABoot 中,除了 XMap 原生的支援以外,還擴充套件了跟 Spring 整合的能力:

  • 透過 XNode  擴展出了 XNodeSpring

  • 透過 XNodeList  擴展出了 XNodeListSpring

  • 透過 XNodeMap 擴展出了 XNodeMapSpring

這部分的擴充套件能力,讓擴充套件點的能力更加豐富,描述物件中可以直接指向一個 SpringBean (使用者配置 bean 的名字,SOFABoot 會根據名字從 Spring 背景關係中獲取到 bean)。

Datasource 擴充套件點案例

基於前小結對於 XMAP 的擴充套件的介紹以及開篇的案例, 這裡舉一個使用 XNodeSpring 的例子,來實現 Spring 背景關係隔離場景對於資料源 bean 的擴充套件。依然是前文描述的三個步驟:

1、輔助介面和類

1、定義一個 DatasourceBean ,並且提供一個 getDatasource 方法,用於獲取 資料源實體。

  1.  

    public interface DatasourceBean {

     

  2.  

       void getDatasource();

     

  3.  

    }

     

2、定義一個 DefaultDataSourceBean ,作為 DatasourceBean 介面的預設實現。

  1.  

    public class DefaultDataSourceBean implements DatasourceBean {

     

  2.  

       @Override

     

  3.  

       public void getDatasource() {

     

  4.  

           System.out.println("mysql datasource");

     

  5.  

       }

     

  6.  

    }

     

2、定義提供擴充套件能力的 DatasourceExtension Bean

新建 DatasourceExtension 介面

  1.  

    public interface DatasourceExtension {

  2.    /**

  3.     * 獲取資料源 Bean 實體

  4.     * @return

  5.     */

  6.    DatasourceBean getDatasourceBean();

  7. }

     

新建 DatasourceExtensionImpl 實現類,並且實現 DatasourceExtension 中的 getDatasourceBean 方法,且裡面透過 datasourceBean 去執行獲取資料源實體。

3、定義擴充套件點

定義並且暴露擴充套件點,這裡還需要一個擴充套件點描述。

下麵在 xml 檔案中將此擴充套件點透過 xml 暴露出去,並配置相關預設實現。

以上幾步在此案例工程包括定義提供擴充套件能力的 bean、包括擴充套件點等均在 glmapper-sofa-provider 中完成,作為擴充套件點的提供方。

4、定義擴充套件

這部分實現是需要由業務方來完成,這裡就需要對於 provider 中提供的擴充套件點進行擴充套件,以改變其本身提供的 bean 實體的行為。

擴充套件擴充套件點,這裡我們希望能夠擴充套件對於 oracle 資料源的支援,那麼對於 provider 中提供的預設對 mysql 的支援的 bean 實體就需要被“擴充套件”,此處的擴充套件本身上就是 bean 實體的替換。

首先定義一個 OracleDatasourceBean ,同樣實現 DatasourceBean 這個介面,getDatasource 方法中傳回 oracle 的資料源實體:

  1.  

    public class OracleDatasourceBean implements DatasourceBean {

  2.    @Override

  3.    public void getDatasource() {

  4.        System.out.println("oracle datasource");

  5.    }

  6. }

     

然後在業務模組(本案例在 glmapper-sofa-web 模組下)的 resources/META-INF/spring/web-home.xml 中配置擴充套件的 bean 並且對擴充套件點進行擴充套件。如下:

詳細程式碼見:glmapper-sofa-extension 。

下麵開始啟動專案工程,首先將擴充套件部分註釋掉,執行 http://localhost:8080/extension ,檢視控制檯列印結果如下:

  1.  

    mysql datasource

     

開啟擴充套件部分註釋,重新啟動,掃清地址,檢視控制檯列印結果如下:

  1.  

    oracle datasource

     

那麼這裡可以看到,provider 中提供的資料源 bean 被自定義的 資料源 bean 替換了。實現了在 Spring 背景關係隔離情況下,替換 bean 的操作。

小結

擴充套件點的存在很好的解決了這樣一個問題:需要在另一個模組中對依賴的模組中定義的元件進行定製化。在模組化的場景下,如果能夠允許改變另外一個模組中 bean 的行為,無疑會解決很多棘手的問題。

本文透過一個簡單的 Demo 對 SOFABoot 中擴充套件點進行了演示,本篇基於 SOFABoot 官方檔案,補充了一些使用上的細節以及需要註意的一些坑,希望透過本篇文章可以幫助大家對 SOFABoot 擴充套件點的能力及使用有初步瞭解。

未出正月都是年,這裡給大家拜個晚年,祝大家新年快樂、升職加薪!

文中相關連結

長按關註,獲取分散式架構乾貨

歡迎大家共同打造 SOFAStack https://github.com/alipay

    贊(0)

    分享創造快樂