天天看點

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

SOFABoot 是基于 Spring Boot 的一套研發架構。

在完全相容 Spring Boot 的基礎上,SOFABoot 還提供了啟動期監控檢查,上下文隔離,子產品化開發,類隔離,日志空間隔離等等能力。

SOFABoot 位址:

https://github.com/alipay/sofa-boot 本文工程案例: https://github.com/glmapper/glmapper-sofa-extension

春節小長假還沒感覺就過去了,對于“熱愛工作”的我,也早早的回到了工作崗位;感受下假期中的我和上班時的我。

後面拿槍的就是"逼着"我寫文章的五花肉,上次 SOFATracer 采樣用的是刀,這次用了槍!
SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結
SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

子產品化與擴充點

言歸正傳,節前 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 應用
官方文檔及案例

 中給的比較複雜,包含了多種使用服務釋出和引用的方式,這裡我使用了最新提供的基于注解的方式來實作;擷取

本文工程案例

擴充點基本使用

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

  • 定義提供擴充能力的 bean
  • 定義擴充點
  • 定義擴充并使用

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

定義提供擴充能力的 bean

本案例工程中,是将 glmapper-sofa-provider 作為服務提供方,是以也在此子產品下定義一個具有擴充能力的 bean 。

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

定義這個接口的實作:

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

在子產品的 Spring 配置檔案 resources/META-INF/service-provider.xml  中,我們把這個 bean 給配置起來:

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

在上面的 bean 中有一個字段 word ,在實際中,我們希望這個字段能夠被其他的子產品自定義進行覆寫,這裡我們将其以擴充點的形式暴露出來。這裡先定義一個類去描述這個擴充點:

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

然後在子產品的 Spring 配置檔案 resources/META-INF/service-provider.xml  中定義擴充點:

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結
  • name   :為擴充點的名字
  • ref       :為擴充點所作用在的 bean
  • object :為擴充點的貢獻點具體的描述,這個描述是通過 XMap 的方式來進行的(XMap 的作用是将 Java 對象和 XML 檔案進行映射,這裡建議通過在網上搜尋下 XMap 的文檔來了解 XMap)

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

定義擴充

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

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結
  • bean     :為擴充所作用在的 bean
  • point    :為擴充點的名字
  • content  裡面的内容為擴充的定義,會通過 XMap 将内容解析為:擴充點的貢獻點具體的描述對象,在這裡即為 com.glmapper.bridge.extension.ExtensionDescriptor 對象
需要注意一點,glmapper-sofa-web 子產品不是一個 SOFABoot 子產品,這裡留坑。

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

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

啟動運作,結果抛了一個錯:

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

沒有找到 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 檔案位置,否則工程可以正常運作,但是基于此工程擴充點擴充的能力是無效的。

registerExtension 

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

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

最開始對這個方法我也很迷糊,因為我并沒有用到。既然自己沒用到,那一定是架構自己用到了。有興趣的同學可以自己斷點下這段邏輯。實際上 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、定義一個 DatasourceBean ,并且提供一個 getDatasource 方法,用于擷取 資料源執行個體。

public interface DatasourceBean {
    void getDatasource();
}           

2、定義一個 DefaultDataSourceBean ,作為 DatasourceBean 接口的預設實作。

public class DefaultDataSourceBean implements DatasourceBean {
    @Override
    public void getDatasource() {
        System.out.println("mysql datasource");
    }
}           

定義提供擴充能力的 DatasourceExtension Bean

建立 DatasourceExtension 接口

public interface DatasourceExtension {
    /**
     * 擷取資料源 Bean 執行個體
     * @return
     */
    DatasourceBean getDatasourceBean();
}           

建立 DatasourceExtensionImpl 實作類,并且實作 DatasourceExtension 中的 getDatasourceBean 方法,且裡面通過 datasourceBean 去執行擷取資料源執行個體。

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

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

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

下面在 xml 檔案中将此擴充點通過 xml 暴露出去,并配置相關預設實作。

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結

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

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

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

首先定義一個 OracleDatasourceBean ,同樣實作 DatasourceBean 這個接口,getDatasource 方法中傳回 oracle 的資料源執行個體:

public class OracleDatasourceBean implements DatasourceBean {
    @Override
    public void getDatasource() {
        System.out.println("oracle datasource");
    }
}           

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

SOFABoot 擴充點初體驗 | SOFALab 實踐系列子產品化與擴充點案例背景構模組化塊化工程擴充點基本使用XMap 支援和擴充Datasource 擴充點案例小結
詳細代碼見: glmapper-sofa-extension  。

下面開始啟動項目工程,首先将擴充部分注釋掉,執行

http://localhost:8080/extension

,檢視控制台列印結果如下:

mysql datasource           

打開擴充部分注釋,重新啟動,重新整理位址,檢視控制台列印結果如下:

oracle datasource           

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

小結

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

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

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

文中相關連結

SOFABoot  2.6.x 系列版本:

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

剖析 | 詳談 SOFABoot 子產品化原理:

https://mp.weixin.qq.com/s/7WAClC-f9mM7-_WIa10M_g

實操 | 基于 SOFABoot 進行子產品化開發:

https://mp.weixin.qq.com/s/-7_wXRcvWpt6JwsBIOkQjA

SOFABoot 官方文檔及案例:

https://www.sofastack.tech/sofa-boot/docs/extension

nuxeo 項目:

https://github.com/nuxeo/nuxeo