全部产品
云市场

隔离功能扩展

更新时间:2020-06-02 11:07:51

SOFABoot 支持模块化隔离,在实际的使用场景中,一个模块中的 bean 有时候需要开放一些入口,供另外一个模块扩展。SOFABoot 借鉴和使用了 Nuxeo Runtime 项目以及 Nuxeo 项目,并在其基础上进行扩展,与 Spring 融合,提供扩展点能力。

下文涉及的概念,说明如下:

  • 扩展点:指暴露给其它模块,可以用来进行扩展的 Bean。
  • 贡献点:扩展点 Bean 中的变量,其值可以被其它模块中的自定义变量覆盖。
  • 贡献点描述类:对贡献点进行描述,并让其暴露给其它模块的类。
  • XMap:是一个库,通过在 Java 对象上使用注解,实现 XML 元素和 Java 对象的映射。该库目前通过 Nuxeo Runtime 进行使用,可以让扩展贡献点的使用更便捷。同时,该库与 Nuxeo Runtime 完全独立,也可以被任意类型的 Java 应用程序使用。

下文内容主要包括下述 2 方面:

实现扩展能力

下文通过 XML 配置的方式,对如何实现 SOFABoot 的扩展能力进行说明。通过 XML 实现扩展的主要逻辑如下:

  • 扩展点 Bean MyExtensionImpl 通过贡献点描述类 ContributionDescriptor 及其 XML 配置,被注册到 SOFABoot 的 Runtime 里面,成为其一个组件。
  • 程序在对 XML 配置进行解析时,MyExtensionImpl 这个 Bean 中的贡献点 word 变量就被覆盖为 <sofa:content><value> 对应的 Khotyn Huang这个新值了。

XML 配置实现扩展示意图XML 扩展实现

扩展能力的实现,主要包括以下 3 个步骤:

  1. 定义扩展点 Bean
  2. 定义贡献点描述类
  3. 配置扩展

定义扩展点 Bean

下文示例定义了一个 Bean,并对其私有变量 word 进行暴露,作为贡献点,使其能够被其他模块自定义变量覆盖。

定义步骤示例如下:

  1. 创建一个接口:

    1. public interface MyExtension {
    2. String say();
    3. }
  2. 创建该接口的实现:

    1. public class MyExtensionImpl implements MyExtension {
    2. private String word;
    3. @Override
    4. public String say() {
    5. return word;
    6. }
    7. public void setWord(String word) {
    8. this.word = word;
    9. }
    10. /**
    11. * 只设置了一个贡献点,该方法还比较简单,随着贡献点增多,方法会变复杂。
    12. */
    13. public void registerExtension(Extension extension) {
    14. Object[] contributions = extension.getContributions();
    15. String extensionPoint = extension.getExtensionPoint();
    16. setWord(((ContributionDescriptor) contributions[0]).getValue());
    17. }
    18. }

    方法说明
    关于方法 registerExtension(),说明如下:

    • 其为组件方法,SOFABoot 框架将会调用这个方法进行扩展点设置,必不可少。
    • 所传递参数 Extension 为 SOFABoot 提供的扩展点描述类。
  3. 在模块的 xml 配置文件中,配置这个 bean:

    1. <bean id="myExtension" class="com.alipay.helloword.biz.service.impl.MyExtensionImpl">
    2. <property name="word" value="Hello, world"/>
    3. </bean>

定义贡献点描述类

定义步骤示例如下:

  1. 创建贡献点描述类:

    1. @XObject("word")
    2. public class ContributionDescriptor {
    3. @XNode("value")
    4. private String value;
    5. public String getValue() {
    6. return value;
    7. }
    8. }
  2. 在 XML 中配置该贡献点描述类:

    1. <sofa:extension-point name="MyExtensionPoint" ref="myExtension">
    2. <sofa:object class="com.alipay.helloword.biz.service.ContributionDescriptor"/>
    3. </sofa:extension-point>

    字段说明

    • name: 为扩展点的名字。
    • ref: 为扩展点 bean 的 id。
    • <sofa:object> 子元素:定义的是扩展点的贡献点(Contribution),可以定义多个贡献点。
    • class 属性:对应的是贡献点描述类,是对贡献点的描述,这种描述是通过 XMap 的方式来进行的,可以将 Java 对象和 XML 文件进行映射。

配置扩展

通过对 xml 的配置来实现对隔离功能的扩展。示例如下:

  1. <sofa:extension bean="myExtension" point="MyExtensionPoint">
  2. <sofa:content>
  3. <!-- 这里 word 对应于 ContributionDescriptor 中的 @XObject 后面声明的字符串 -->
  4. <word>
  5. <!-- 这里 value 对应于 ContributionDescriptor 中的 @XNode 后面声明的字符串-->
  6. <value>Khotyn Huang</value>
  7. </word>
  8. </sofa:content>
  9. </sofa:extension>

字段说明

  • word: 对应于 ContributionDescriptor 中 @XObject 后面声明的字符串。
  • value:对应于 ContributionDescriptor 中的 @XNode 后面声明的字符串。
  • bean:为扩展点 bean。
  • point:为扩展点的名字。
  • <sofa:content>: 里面的内容为扩展内容的定义,其会通过 XMap 将内容解析为扩展描述类对象,此处即为 com.alipay.sofa.boot.test.extension.ExtensionDescriptor 对象。

高级扩展能力实例

SOFABoot 的扩展能力包括:

  • 支持 XMap 原生描述能力,包括:
    • List
    • Map
  • 扩展了 Spring 集成能力,包括:
    • 通过 XNode 扩展出了 XNodeSpring
    • 通过 XNodeList 扩展出了 XNodeListSpring
    • 通过 XNodeMap 扩展出了 XNodeMapSpring

这部分的扩展能力,使扩展点的能力更加丰富,描述对象中可以直接指向一个 SpringBean。用户配置 bean 的名字,SOFABoot 会根据名字从 Spring 上下文中获取到 bean。

下文以使用 XNodeListSpring 为例,对上述高级扩展功能进行说明。实现方式有 2 种:

XML 配置实现

主要步骤如下:

  1. 定义扩展点 Bean
  2. 定义贡献点描述类
  3. 配置扩展

定义扩展点 Bean

定义步骤示例如下:

  1. 创建一个接口:

    1. package com.alipay.sofa.boot.test;
    2. public interface IExtension {
    3. List<SimpleSpringListBean> getSimpleSpringListBeans();
    4. }

    说明

    • 本接口返回一个 list,目标是这个 list 能够通过扩展的方式填充;
    • SimpleSpringListBean 可根据需求来定义,此处假设定义了一个空实现。
  2. 创建该接口实现类:

    1. public class IExtensionImpl implements IExtension {
    2. private List<SimpleSpringListBean> simpleSpringListBeans = new ArrayList<>();
    3. @Override
    4. public List<SimpleSpringListBean> getSimpleSpringListBeans() {
    5. return simpleSpringListBeans;
    6. }
    7. public void registerExtension(Extension extension) throws Exception {
    8. Object[] contributions = extension.getContributions();
    9. String extensionPoint = extension.getExtensionPoint();
    10. if (contributions == null) {
    11. return;
    12. }
    13. for (Object contribution : contributions) {
    14. if ("testSpringList".equals(extensionPoint)) {
    15. simpleSpringListBeans.addAll(((SpringListContributionDescriptor) contribution)
    16. .getValues());
    17. }
    18. }
    19. }
    20. }

    说明:以 simpleSpringListBeans 为贡献点。

  3. 在模块的 Spring 配置文件中,配置这个 bean:
    1. <bean id="iExtension" class="com.alipay.sofa.runtime.integration.extension.bean.IExtensionImpl"/>

定义贡献点描述类

定义步骤如下:

  1. 创建一个贡献点描述类:

    1. @XObject("testSpringList")
    2. public class SpringListContributionDescriptor {
    3. @XNodeListSpring(value = "value", componentType = SimpleSpringListBean.class, type = ArrayList.class)
    4. private List<SimpleSpringListBean> values;
    5. public List<SimpleSpringListBean> getValues() {
    6. return values;
    7. }
    8. }

    说明
    @XNodeListSpring:当在 XML 中配置相应 bean 的名字时, SOFABoot 会从 Spring 上下文中获取到相应的 bean 实例。

  2. 在 XML 中定义该贡献点描述类:

    1. <sofa:extension-point name="testSpringList" ref="iExtension">
    2. <sofa:object class="com.alipay.sofa.runtime.integration.extension.descriptor.SpringListContributionDescriptor"/>
    3. </sofa:extension-point>

配置扩展

通过对 xml 的配置来实现对隔离功能的扩展。

  1. <bean id="simpleSpringListBean1" class="com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringListBean" />
  2. <bean id="simpleSpringListBean2" class="com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringListBean" />
  3. <sofa:extension bean="iExtension" point="testSpringList">
  4. <sofa:content>
  5. <testSpringList>
  6. <value>simpleSpringListBean1</value>
  7. <value>simpleSpringListBean2</value>
  8. </testSpringList>
  9. </sofa:content>
  10. </sofa:extension>

说明:对配置内容说明如下:

  • 定义了两个 bean,并将其放入扩展定义中。
  • 调用 iExtensiongetSimpleSpringListBeans 方法,将会看到其中包含了通过扩展方式添加的两个 bean。

客户端编程 API 实现

主要通过 com.alipay.sofa.runtime.api.client.ExtensionClient 来完成下述步骤:

  1. 获取扩展客户端 Bean
  2. 设置扩展点参数
  3. 定义扩展

获取扩展客户端 Bean

需要实现 SOFABoot 中提供的接口 com.alipay.sofa.runtime.api.aware.ExtensionClientAware,示例如下:

  1. public class ExtensionClientBean implements ExtensionClientAware {
  2. private ExtensionClient extensionClient;
  3. @Override
  4. public void setExtensionClient(ExtensionClient extensionClient) {
  5. this.clientFactory = extensionClient;
  6. }
  7. public ExtensionClient getClientFactory() {
  8. return extensionClient;
  9. }
  10. }

设置扩展点参数

  1. ExtensionPointParam extensionPointParam = new ExtensionPointParam();
  2. extensionPointParam.setName("clientValue");
  3. extensionPointParam.setTargetName("iExtension");
  4. extensionPointParam.setTarget(iExtension);
  5. extensionPointParam.setContributionClass(ClientExtensionDescriptor.class);
  6. extensionClient.publishExtensionPoint(extensionPointParam);

通过客户端定义扩展点与通过 XML 时各参数的含义保持一致:

  • name 为扩展点的名字。
  • targetName 为扩展点所作用在的 bean 的名字。
  • target 为扩展点所作用在的 bean。
  • contributionClass 为扩展点的贡献点具体的描述,此处描述的定义示例如下:
  1. @XObject("clientValue")
  2. public class ClientExtensionDescriptor {
  3. @XNode("value")
  4. private String value;
  5. public String getValue() {
  6. return value;
  7. }
  8. }

通过 com.alipay.sofa.runtime.api.client.ExtensionClientpublishExtensionPoint 即可定义扩展点。

定义扩展

  1. DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
  2. DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
  3. Document doc = dBuilder.parse(new File(Thread.currentThread().getContextClassLoader()
  4. .getResource("META-INF/extension/extension.xml").toURI()));
  5. ExtensionParam extensionParam = new ExtensionParam();
  6. extensionParam.setTargetName("clientValue");
  7. extensionParam.setTargetInstanceName("iExtension");
  8. extensionParam.setElement(doc.getDocumentElement());
  9. extensionClient.publishExtension(extensionParam);

通过客户端定义扩展与通过 XML 时各参数的含义保持一致:

  • targetInstanceName 为扩展所作用在的 bean。
  • targetName 为扩展点的名字。
  • element 里面的内容为扩展的定义,此处需要传入 Element 对象,通过从 XML 中读取,示例内容如下:
  1. <clientValue>
  2. <value>SOFABoot Extension Client Test</value>
  3. </clientValue>

通过 com.alipay.sofa.runtime.api.client.ExtensionClientpublishExtension 即可定义扩展。

客户端限制

由于扩展的定义强依赖 XML,因此虽然此处通过客户端发布扩展点和扩展,但是扩展自身的内容还是需要 XML 来描述,并没有真正做到只使用客户端。