SOFABoot 扩展点初体验 | SOFALab 实践系列

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: SOFABoot 是基于 Spring Boot 的一套研发框架。 在完全兼容 Spring Boot 的基础上,SOFABoot 还提供了启动期监控检查,上下文隔离,模块化开发,类隔离,日志空间隔离等等能力。

SOFABoot 是基于 Spring Boot 的一套研发框架。

在完全兼容 Spring Boot 的基础上,SOFABoot 还提供了启动期监控检查,上下文隔离,模块化开发,类隔离,日志空间隔离等等能力。

SOFABoot 地址:
https://github.com/alipay/sofa-boot


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

春节小长假还没感觉就过去了,对于“热爱工作”的我,也早早的回到了工作岗位;感受下假期中的我和上班时的我。

后面拿枪的就是"逼着"我写文章的五花肉,上次 SOFATracer 采样用的是刀,这次用了枪!

image.png    image.png

模块化与扩展点

言归正传,节前 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 。
image.png

定义这个接口的实现:
image.png

在模块的 Spring 配置文件 resources/META-INF/service-provider.xml  中,我们把这个 bean 给配置起来:
image.png

定义扩展点

在上面的 bean 中有一个字段 word ,在实际中,我们希望这个字段能够被其他的模块自定义进行覆盖,这里我们将其以扩展点的形式暴露出来。这里先定义一个类去描述这个扩展点:
image.png

然后在模块的 Spring 配置文件 resources/META-INF/service-provider.xml  中定义扩展点:

image.png

  • name   :为扩展点的名字
  • ref       :为扩展点所作用在的 bean
  • object :为扩展点的贡献点具体的描述,这个描述是通过 XMap 的方式来进行的(XMap 的作用是将 Java 对象和 XML 文件进行映射,这里建议通过在网上搜索下 XMap 的文档来了解 XMap)

至此服务提供端已经暴露出了扩展点,那么在服务使用端,也就是需要扩展这个 bean 的使用方就可以扩展这个bean 了。

定义扩展

上述已经将扩展点定义好了,此时我们就可以对这个 bean 进行扩展了。扩展是具体业务使用方来做的事,在本案例中,glmapper-sofa-web 模块作为使用服务使用方,因此在 resources/META-INF/spring/web-home.xml 下进行扩展定义:
image.png

  • bean     :为扩展所作用在的 bean
  • point    :为扩展点的名字
  • content  里面的内容为扩展的定义,会通过 XMap 将内容解析为:扩展点的贡献点具体的描述对象,在这里即为 com.glmapper.bridge.extension.ExtensionDescriptor 对象

需要注意一点,glmapper-sofa-web 模块不是一个 SOFABoot 模块,这里留坑。

编写一个 TestController 类,这里最先参考的是 SOFABoot 测试用例中的写法,如下:
image.png

启动运行,结果抛了一个错:

image.png

没有找到 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 这个类中有一个特殊的方法,在整个案例演示中其实都是没有用到的。
image.png

最开始对这个方法我也很迷糊,因为我并没有用到。既然自己没用到,那一定是框架自己用到了。有兴趣的同学可以自己断点下这段逻辑。实际上 SOFABoot 在解析到贡献点时,会调用被扩展 bean 的 registerExtension 方法,其中包含了用户定义的贡献点处理逻辑。在上述的例子中,获取用户定义的 value 值,并将其设置到 word 字段中覆盖 bean 中原始定义的值,最后调用 extension.say() 方法,可以看到返回扩展中定义的值: newValue 。

XMap 支持和扩展

上述的例子中只是一个很简单的扩展,其实 XMap 包含了非常丰富的描述能力,包括 ListMap 等,这些可以通过查看 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 去执行获取数据源实例。

image.png

定义扩展点

定义并且暴露扩展点,这里还需要一个扩展点描述。
image.png
下面在 xml 文件中将此扩展点通过 xml 暴露出去,并配置相关默认实现。
image.png

以上几步在此案例工程包括定义提供扩展能力的 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 并且对扩展点进行扩展。如下:
image.png

详细代码见:glmapper-sofa-extension 。

下面开始启动项目工程,首先将扩展部分注释掉,执行 http://localhost:8080/extension ,查看控制台打印结果如下:

mysql datasource

打开扩展部分注释,重新启动,刷新地址,查看控制台打印结果如下:

oracle datasource

那么这里可以看到,provider 中提供的数据源 bean 被自定义的 数据源 bean 替换了。实现了在 Spring 上下文隔离情况下,替换 bean 的操作。

小结

扩展点的存在很好的解决了这样一个问题:需要在另一个模块中对依赖的模块中定义的组件进行定制化。在模块化的场景下,如果能够允许改变另外一个模块中 bean 的行为,无疑会解决很多棘手的问题。

本文通过一个简单的 Demo 对 SOFABoot 中扩展点进行了演示,本篇基于 SOFABoot 官方文档,补充了一些使用上的细节以及需要注意的一些坑,希望通过本篇文章可以帮助大家对 SOFABoot 扩展点的能力及使用有初步了解。

未出正月都是年,这里给大家拜个晚年,祝大家新年快乐、升职加薪!

文中相关链接

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

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

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
SQL IDE Java
入门Cloud Toolkit:简化你的Java应用开发与部署流程
【10月更文挑战第19天】作为一名长期从事Java开发的程序员,我一直致力于寻找能够简化日常开发工作的工具。在众多工具中,阿里巴巴推出的Cloud Toolkit引起了我的注意。这款免费的插件旨在帮助开发者更轻松地进行开发、测试及部署工作,尤其是在与云服务交互时表现尤为出色。本文将从个人的角度出发,介绍Cloud Toolkit的基本功能及其使用技巧,希望能帮助初学者快速上手这款实用工具。
29 1
|
6月前
|
Kubernetes Cloud Native Java
quarkus 搭建与基础开发环境配置总结
quarkus 搭建与基础开发环境配置总结
48 0
|
7月前
|
设计模式 Java 机器人
学习笔记之二《SpringBoot3-核心特性》
学习笔记之二《SpringBoot3-核心特性》
148 0
|
消息中间件 Cloud Native Java
【深入浅出SpringCloud原理及实战】「SpringCloud-Alibaba系列」微服务模式搭建系统基础架构实战指南及版本规划踩坑分析
【深入浅出SpringCloud原理及实战】「SpringCloud-Alibaba系列」微服务模式搭建系统基础架构实战指南及版本规划踩坑分析
715 1
【深入浅出SpringCloud原理及实战】「SpringCloud-Alibaba系列」微服务模式搭建系统基础架构实战指南及版本规划踩坑分析
|
运维 监控 Java
最近超火的SOFABoot是个什么东西?带你快速入门SOFABoot
最近超火的SOFABoot是个什么东西?带你快速入门SOFABoot
最近超火的SOFABoot是个什么东西?带你快速入门SOFABoot
|
安全 Java 数据安全/隐私保护
从零开始搭建博客04----分模块以及搭建微服务框架(Finchley版本)
前面几篇我们完成了博客的大部分功能。接下来我们将对博客模块进行拆分,以此巩固所学的微服务知识。
170 0
从零开始搭建博客04----分模块以及搭建微服务框架(Finchley版本)
|
Java Unix API
Quarkus项目配置方式详解
Quarkus 可以从多个地方获取项目的配置,它读取配置优先级入下图,在下面的优先级中,一旦读取到某个配置,就不会再继续读取后面配置中的这个配置了。
949 0
|
SQL 开发框架 前端开发
SpringCloud微服务实战——搭建企业级开发框架(二十二):基于MybatisPlus插件TenantLineInnerInterceptor实现多租户功能
多租户技术的基本概念:   多租户技术(英语:multi-tenancy technology)或称多重租赁技术,是一种软件架构技术,它是在探讨与实现如何于多用户的环境下共用相同的系统或程序组件,并且仍可确保各用户间数据的隔离性。   在云计算的加持之下,多租户技术被广为运用于开发云各式服务,不论是IaaS,PaaS还是SaaS,都可以看到多租户技术的影子。
953 54
|
SQL 缓存 开发框架
SpringCloud微服务实战——搭建企业级开发框架(二十八):扩展MybatisPlus插件DataPermissionInterceptor实现数据权限控制
一套完整的系统权限需要支持功能权限和数据权限,前面介绍了系统通过RBAC的权限模型来实现功能的权限控制,这里我们来介绍,通过扩展Mybatis-Plus的插件DataPermissionInterceptor实现数据权限控制。   简单介绍一下,所谓功能权限,顾名思义是指用户在系统中拥有对哪些功能操作的权限控制,而数据权限是指用户在系统中能够访问哪些数据的权限控制,数据权限又分为行级数据权限和列级数据权限。
1595 27
SpringCloud微服务实战——搭建企业级开发框架(二十八):扩展MybatisPlus插件DataPermissionInterceptor实现数据权限控制
|
Java Maven Spring
SpringBoot多环境适配之道
本文是应用配置中心的第三篇,第一篇介绍了配置演化之路,第二篇介绍了在传统项目中以Maven作为多环境支持的方式。这篇将以SpringBoot为例,重点阐述在SpringBoot中如何适配多环境。
189 0
SpringBoot多环境适配之道