返回介绍

SOFABoot 拓展点

发布于 2021-04-06 06:22:51 字数 10074 浏览 1232 评论 0 收藏 0

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

使用

在 SOFABoot 中使用扩展点能力,需要以下三个步骤:

定义提供扩展能力的 bean

在使用 SOFABoot 扩展点能力时,首先需要定一个需要被扩展的 bean,先定一个接口:

package com.alipay.sofa.boot.test;

public interface IExtension {

    String say();
}

定义这个接口的实现:

package com.alipay.sofa.boot.test.impl;

public class ExtensionImpl implements IExtension {

    private String word;

    @Override
    public String say() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }

    public void registerExtension(Extension extension) throws Exception {
        Object[] contributions = extension.getContributions();
        String extensionPoint = extension.getExtensionPoint();

        if (contributions == null) {
            return;
        }

        for (Object contribution : contributions) {
            if ("word".equals(extensionPoint)) {
                setWord(((ExtensionDescriptor) contribution).getValue());
            }
        }
    }
}

在这里可以看到有一个方法:registerExtension ,暂时可以先不用管这个方法,后续会介绍其具体的作用。

在模块的 Spring 配置文件中,我们把这个 bean 给配置起来:

<bean id="extension" class="com.alipay.sofa.boot.test.impl.ExtensionImpl">
    <property name="word" value="Hello, world"/>
</bean>

定义扩展点

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

首先需要一个类去描述这个扩展点:

@XObject("word")
public class ExtensionDescriptor {

    @XNode("value")
    private String value;

    public String getValue() {
        return value;
    }
}

然后在 xml 中定义扩展点:

<sofa:extension-point name="word" ref="extension">
    <sofa:object class="com.alipay.sofa.boot.test.extension.ExtensionDescriptor"/>
</sofa:extension-point>

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

定义扩展

上述已经将扩展点定义好了,此时我们就可以对这个 bean 进行扩展了:

<sofa:extension bean="extension" point="word">
    <sofa:content>
        <word>
            <value>newValue</value>
        </word>
    </sofa:content>
</sofa:extension>

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

到这里,我们可以回头看一开始在 com.alipay.sofa.boot.test.impl.ExtensionImpl 中定义的 registerExtension 方法了,SOFABoot 在解析到贡献点时,会调用被扩展 bean 的 registerExtension 方法,其中包含了用户定义的贡献点处理逻辑,在上述的例子中,获取用户定义的 value 值,并将其设置到 word 字段中覆盖 bean 中原始定义的值。

此时,调用 extension bean 的 say() 方法,可以看到返回扩展中定义的值: newValue 。

XMap 支持和扩展

上述的例子中只是一个很简单的扩展,其实 XMap 包含了非常丰富的描述能力,包括 List, Map 等,这些可以通过查看 XMap 的文档来了解。

在 SOFABoot 中,除了 XMap 原生的支持以外,还扩展了跟 Spring 集成的能力: - 通过 XNode 扩展出了 XNodeSpring - 通过 XNodeList 扩展出了 XNodeListSpring - 通过 XNodeMap 扩展出了 XNodeMapSpring

这部分的扩展能力,让扩展点的能力更加丰富,描述对象中可以直接指向一个 SpringBean(用户配置 bean 的名字,SOFABoot 会根据名字从 spring 上下文中获取到 bean),这里举一个使用 XNodeListSpring 的例子,依然是上述描述的三个步骤:

定义提供扩展能力的 bean

接口定义:

在这个接口里,返回一个 list,目标是这个 list 能够被通过扩展的方式填充

package com.alipay.sofa.boot.test;

public interface IExtension {

    List<SimpleSpringListBean> getSimpleSpringListBeans();
}

其中 SimpleSpringListBean 可根据需求来定义,这里我们假设定义了一个空实现

实现:

public class IExtensionImpl implements IExtension {
    private List<SimpleSpringListBean>       simpleSpringListBeans  = new ArrayList<>();

    @Override
    public List<SimpleSpringListBean> getSimpleSpringListBeans() {
        return simpleSpringListBeans;
    }
    
     public void registerExtension(Extension extension) throws Exception {
        Object[] contributions = extension.getContributions();
        String extensionPoint = extension.getExtensionPoint();

        if (contributions == null) {
            return;
        }

        for (Object contribution : contributions) {
            if ("testSpringList".equals(extensionPoint)) {
                simpleSpringListBeans.addAll(((SpringListExtensionDescriptor) contribution)
                    .getValues());
            }
        }
    }
}

可以看到:在 registerExtension 中将通过贡献点定义的 bean 加入到 list 中,以达到让用户扩展这个 list 的能力。

在模块的 Spring 配置文件中,我们把这个 bean 给配置起来:

    <bean id="iExtension" class="com.alipay.sofa.runtime.integration.extension.bean.IExtensionImpl"/>

定义扩展点

首先需要一个对象去描述:

@XObject("testSpringList")
public class SpringListExtensionDescriptor {

    @XNodeListSpring(value = "value", componentType = SimpleSpringListBean.class, type = ArrayList.class)
    private List<SimpleSpringListBean> values;

    public List<SimpleSpringListBean> getValues() {
        return values;
    }
}

在这里用到了 @XNodeListSpring ,当在 xml 中配置相应 bean 的名字时, SOFABoot 会从 spring 上下文中获取到相应的 bean 实例

在 xml 中将这个扩展点定义出来

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

定义扩展

<bean id="simpleSpringListBean1" class="com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringListBean" />
<bean id="simpleSpringListBean2" class="com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringListBean" />

<sofa:extension bean="iExtension" point="testSpringList">
    <sofa:content>
        <testSpringList>
            <value>simpleSpringListBean1</value>
            <value>simpleSpringListBean2</value>
        </testSpringList>
    </sofa:content>
</sofa:extension>

在定义扩展时,首先定义了两个 bean,然后将它们放入扩展定义中。

此时,调用 iExtensiongetSimpleSpringListBeans 方法,可以看到其中包含了通过扩展方式添加的两个 bean。

通过客户端方式定义扩展点和扩展

除了通过上述 xml 的方式配置定义扩展点和扩展以外,SOFABoot 中还提供了 com.alipay.sofa.runtime.api.client.ExtensionClient 的方式来定义扩展点和扩展

获取 ExtensionClient

获取 com.alipay.sofa.runtime.api.client.ExtensionClient 比较简单,SOFABoot 中提供了 com.alipay.sofa.runtime.api.aware.ExtensionClientAware

public class ExtensionClientBean implements ExtensionClientAware {
 
     private ExtensionClient extensionClient;
 
     @Override
     public void setExtensionClient(ExtensionClient extensionClient) {
         this.clientFactory = extensionClient;
     }
 
     public ExtensionClient getClientFactory() {
         return extensionClient;
     }
}

ExtensionClientBean 定义为 spring bean 即可

定义扩展点

ExtensionPointParam extensionPointParam = new ExtensionPointParam();
extensionPointParam.setName("clientValue");
extensionPointParam.setTargetName("iExtension");
extensionPointParam.setTarget(iExtension);
extensionPointParam.setContributionClass(ClientExtensionDescriptor.class);
extensionClient.publishExtensionPoint(extensionPointParam);

通过客户端和通过 xml 注册扩展点时各参数的含义保持一致: 其中: - name 为扩展点的名字 - targetName 为扩展点所作用在的 bean 的名字 - target 为扩展点所作用在的 bean - contributionClass 为扩展点的贡献点具体的描述,这里描述的定义示例如下:

@XObject("clientValue")
public class ClientExtensionDescriptor {
    @XNode("value")
    private String value;

    public String getValue() {
        return value;
    }
}

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

定义扩展

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

通过客户端和通过 xml 注册扩展点时各参数的含义保持一致: - targetInstanceName 为扩展所作用在的 bean - targetName 为扩展点的名字 - element 里面的内容为扩展的定义,这里需要传入 Element 对象,通过从 xml 中读取,这里示例子的内容如下:

<clientValue>
    <value>SOFABoot Extension Client Test</value>
</clientValue>

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

客户端限制

可以看到由于扩展的定义强依赖 XML ,故而虽然这里是通过客户端发布扩展点和扩展,但是扩展自身的内容还是需要 XML 来描述,并没有真正做到只使用客户端,此部分欢迎有兴趣的同学提改进意见。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文