SOFABoot 擴展點初體驗 | SOFALab 實踐系列

SOFABoot 是基於 Spring Boot 的一套研發框架。
在徹底兼容 Spring Boot 的基礎上,SOFABoot 還提供了啓動期監控檢查,上下文隔離,模塊化開發,類隔離,日誌空間隔離等等能力。
SOFABoot 地址: https://github.com/alipay/sofa-boot
本文工程案例: github.com/glmapper/gl…


模塊化與擴展點

春節前 SOFABoot 發佈了 2.6.x 系列版本,新特性也是至關給力,這裏簡單羅列下新特性:html

  • 支持擴展和擴展點
  • 在刷新上下文期間支持 spring bean 的並行初始化
  • 支持使用註解方式發佈 JVM 服務

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

在 Spring 上下文隔離的狀況下,各個上下文之間的 bean 是互不可見;SOFABoot 中經過發佈 JVM 服務的方式使得不一樣模塊 bean 之間的訪問得以實現。可是同時又帶來了另一個問題,若是一個模塊以獨立 jar 的方式對外提供 api ,那麼對於其餘依賴此模塊的模塊來講,就沒法去改變這個模塊中的 bean 實例行爲。mysql

在實際的使用場景中,一個模塊中的 bean 有時候須要開放一些入口,供另一個模塊擴展。SOFABoot 借鑑和使用了 Nuxeo Runtime 項目 以及 nuxeo 項目,並在上面擴展,與 Spring 融合,提供了擴展點的能力。git

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


案例背景

這裏先拋出一個例子,如今有一個三方 jar ,它定義了獲取數據源接口的頂層抽象;不一樣的業務方若是依賴這個 jar,則須要本身提供一個數據源的實現,固然其自己提供了默認實現(假設是 mysql)。基於此咱們大概可以想到的方式就是基於 SPI 來提供這種擴展能力,可是對於在 Spring 上下文隔離的狀況下,業務方的 Spring 上下文是沒法與引入 jar 的上下文共享 bean 的,這樣天然也就沒法實現對原有數據源實現的擴展。web

那麼這裏咱們就能夠選擇使用 SOFABoot 擴展點來實現,這裏有兩個比較重要的概念,也是擴展點區別於 SPI 的主要特性:spring

  • 能夠在基於 Spring 上下文隔離的狀況下實現擴展
  • 擴展的是 Spring Bean

下面基於這兩個點,來完成自定義擴展點的一個案例。在實現上述案例以前咱們須要先構建一個基於 Spring 上下文隔離的模塊化工程,而後再簡單介紹下擴展點的基本使用方式。sql


構建模塊化工程

SOFABoot 開源版本中並無給出擴展點相關的案例工程,只是在測試用例中進行了詳細的測試,有興趣的同窗能夠看下相關測試用例代碼。實際上測試用例中也沒有涉及到在模塊化的場景下使用擴展點,測試用例都是基於同一個Spring 上下文來完成的。本篇文章將先搭建一個簡單的模塊化工程,而後基於此工程來實現擴展點的能力。api

本工程主要包括 4 個模塊:bash

  • 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 。


定義這個接口的實現:


在模塊的 Spring 配置文件 resources/META-INF/service-provider.xml 中,咱們把這個 bean 給配置起來:


定義擴展點

在上面的 bean 中有一個字段 word ,在實際中,咱們但願這個字段可以被其餘的模塊自定義進行覆蓋,這裏咱們將其以擴展點的形式暴露出來。這裏先定義一個類去描述這個擴展點:


而後在模塊的 Spring 配置文件 resources/META-INF/service-provider.xml 中定義擴展點:

  • name :爲擴展點的名字
  • ref :爲擴展點所做用在的 bean
  • object :爲擴展點的貢獻點具體的描述,這個描述是經過 XMap 的方式來進行的(XMap 的做用是將 Java 對象和 XML 文件進行映射,這裏建議經過在網上搜索下 XMap 的文檔來了解 XMap)

至此服務提供端已經暴露出了擴展點,那麼在服務使用端,也就是須要擴展這個 bean 的使用方就能夠擴展這個bean 了。


定義擴展

上述已經將擴展點定義好了,此時咱們就能夠對這個 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 文件位置,不然工程能夠正常運行,可是基於此工程擴展點擴展的能力是無效的。


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 的擴展。依然是前文描述的三個步驟:

輔助接口和類

一、定義一個 DatasourceBean ,而且提供一個 getDatasource 方法,用於獲取 數據源實例。

public interface DatasourceBean {
    void getDatasource();
}複製代碼


二、定義一個 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 去執行獲取數據源實例。


定義擴展點

定義而且暴露擴展點,這裏還須要一個擴展點描述。

下面在 xml 文件中將此擴展點經過 xml 暴露出去,並配置相關默認實現。

以上幾步在此案例工程包括定義提供擴展能力的 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 而且對擴展點進行擴展。以下:

詳細代碼見:glmapper-sofa-extension


下面開始啓動項目工程,首先將擴展部分註釋掉,執行 http://localhost:8080/extension ,查看控制檯打印結果以下:

mysql datasource複製代碼

打開擴展部分註釋,從新啓動,刷新地址,查看控制檯打印結果以下:

oracle datasource複製代碼

那麼這裏能夠看到,provider 中提供的數據源 bean 被自定義的 數據源 bean 替換了。實現了在 Spring 上下文隔離狀況下,替換 bean 的操做。


小結

擴展點的存在很好的解決了這樣一個問題:須要在另外一個模塊中對依賴的模塊中定義的組件進行定製化。在模塊化的場景下,若是可以容許改變另一個模塊中 bean 的行爲,無疑會解決不少棘手的問題。

本文經過一個簡單的 Demo 對 SOFABoot 中擴展點進行了演示,本篇基於 SOFABoot 官方文檔,補充了一些使用上的細節以及須要注意的一些坑,但願經過本篇文章能夠幫助你們對 SOFABoot 擴展點的能力及使用有初步瞭解。

未出正月都是年,這裏給你們拜個晚年,祝你們新年快樂、升職加薪!


文中相關連接

本文工程案例:github.com/glmapper/gl…


SOFABoot 2.6.x 系列版本:github.com/alipay/sofa…

剖析 | 詳談 SOFABoot 模塊化原理:mp.weixin.qq.com/s/7WAClC-f9…

實操 | 基於 SOFABoot 進行模塊化開發:mp.weixin.qq.com/s/-7_wXRcvW…

SOFABoot 官方文檔及案例:www.sofastack.tech/sofa-boot/d…


nuxeo 項目:github.com/nuxeo/nuxeo

公衆號:金融級分佈式架構(Antfin_SOFA)

相關文章
相關標籤/搜索