更新时间:2020-06-02 11:07
SOFABoot 支持模块化隔离,在实际的使用场景中,一个模块中的 bean 有时候需要开放一些入口,供另外一个模块扩展。SOFABoot 借鉴和使用了 Nuxeo Runtime 项目以及 Nuxeo 项目,并在其基础上进行扩展,与 Spring 融合,提供扩展点能力。
下文涉及的概念,说明如下:
下文内容主要包括下述 2 方面:
下文通过 XML 配置的方式,对如何实现 SOFABoot 的扩展能力进行说明。通过 XML 实现扩展的主要逻辑如下:
MyExtensionImpl
这个 Bean 中的贡献点 word
变量就被覆盖为 <sofa:content>
中 <value>
对应的 Khotyn Huang
这个新值了。XML 配置实现扩展示意图
扩展能力的实现,主要包括以下 3 个步骤:
下文示例定义了一个 Bean,并对其私有变量 word 进行暴露,作为贡献点,使其能够被其他模块自定义变量覆盖。
定义步骤示例如下:
创建一个接口:
public interface MyExtension {
String say();
}
创建该接口的实现:
public class MyExtensionImpl implements MyExtension {
private String word;
@Override
public String say() {
return word;
}
public void setWord(String word) {
this.word = word;
}
/**
* 只设置了一个贡献点,该方法还比较简单,随着贡献点增多,方法会变复杂。
*/
public void registerExtension(Extension extension) {
Object[] contributions = extension.getContributions();
String extensionPoint = extension.getExtensionPoint();
setWord(((ContributionDescriptor) contributions[0]).getValue());
}
}
方法说明
关于方法 registerExtension()
,说明如下:
在模块的 xml 配置文件中,配置这个 bean:
<bean id="myExtension" class="com.alipay.helloword.biz.service.impl.MyExtensionImpl">
<property name="word" value="Hello, world"/>
</bean>
定义步骤示例如下:
创建贡献点描述类:
@XObject("word")
public class ContributionDescriptor {
@XNode("value")
private String value;
public String getValue() {
return value;
}
}
在 XML 中配置该贡献点描述类:
<sofa:extension-point name="MyExtensionPoint" ref="myExtension">
<sofa:object class="com.alipay.helloword.biz.service.ContributionDescriptor"/>
</sofa:extension-point>
字段说明:
name
: 为扩展点的名字。ref
: 为扩展点 bean 的 id。<sofa:object> 子元素
:定义的是扩展点的贡献点(Contribution),可以定义多个贡献点。class
属性:对应的是贡献点描述类,是对贡献点的描述,这种描述是通过 XMap 的方式来进行的,可以将 Java 对象和 XML 文件进行映射。通过对 xml 的配置来实现对隔离功能的扩展。示例如下:
<sofa:extension bean="myExtension" point="MyExtensionPoint">
<sofa:content>
<!-- 这里 word 对应于 ContributionDescriptor 中的 @XObject 后面声明的字符串 -->
<word>
<!-- 这里 value 对应于 ContributionDescriptor 中的 @XNode 后面声明的字符串-->
<value>Khotyn Huang</value>
</word>
</sofa:content>
</sofa:extension>
字段说明:
word
: 对应于 ContributionDescriptor 中 @XObject 后面声明的字符串。value
:对应于 ContributionDescriptor 中的 @XNode 后面声明的字符串。bean
:为扩展点 bean。point
:为扩展点的名字。<sofa:content>
: 里面的内容为扩展内容的定义,其会通过 XMap 将内容解析为扩展描述类对象,此处即为 com.alipay.sofa.boot.test.extension.ExtensionDescriptor
对象。SOFABoot 的扩展能力包括:
XNode
扩展出了 XNodeSpring
XNodeList
扩展出了 XNodeListSpring
XNodeMap
扩展出了 XNodeMapSpring
这部分的扩展能力,使扩展点的能力更加丰富,描述对象中可以直接指向一个 SpringBean。用户配置 bean 的名字,SOFABoot 会根据名字从 Spring 上下文中获取到 bean。
下文以使用 XNodeListSpring
为例,对上述高级扩展功能进行说明。实现方式有 2 种:
主要步骤如下:
定义步骤示例如下:
创建一个接口:
package com.alipay.sofa.boot.test;
public interface IExtension {
List<SimpleSpringListBean> getSimpleSpringListBeans();
}
说明:
- 本接口返回一个 list,目标是这个 list 能够通过扩展的方式填充;
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(((SpringListContributionDescriptor) contribution)
.getValues());
}
}
}
}
说明:以
simpleSpringListBeans
为贡献点。
<bean id="iExtension" class="com.alipay.sofa.runtime.integration.extension.bean.IExtensionImpl"/>
定义步骤如下:
创建一个贡献点描述类:
@XObject("testSpringList")
public class SpringListContributionDescriptor {
@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.SpringListContributionDescriptor"/>
</sofa:extension-point>
通过对 xml 的配置来实现对隔离功能的扩展。
<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,并将其放入扩展定义中。
- 调用
iExtension
的getSimpleSpringListBeans
方法,将会看到其中包含了通过扩展方式添加的两个 bean。
主要通过 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;
}
}
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.ExtensionClient
的 publishExtensionPoint
即可定义扩展点。
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.ExtensionClient
的 publishExtension
即可定义扩展。
由于扩展的定义强依赖 XML,因此虽然此处通过客户端发布扩展点和扩展,但是扩展自身的内容还是需要 XML 来描述,并没有真正做到只使用客户端。
在文档使用中是否遇到以下问题
更多建议
匿名提交