通用转换接口设计
目录
- 转换接口的定义
- 转换接口的作用
- 转换接口的使用
- 转换接口的实现
1. 转换接口的定义
接口是系统内部与第三方系统协议数据的相互转换处理过程,转换接口以XML配置的形式把这种处理过程描述出来,方便接口的开发与维护;
2. 转换接口的作用
- 增加开发效率,转换接口抽象出通用的转换处理,开发只需组装对应转换操作即可,减少了重复开发的工作,增加开发效率;
- 降低出错率,统一协议转换之外的处理,接口开发只需配置相应的转换处理,减少代码量,降低出错概率;
- 减少维护成本,接口配置化后,对接口协议的调整,只需要针对接口配置修改即可;
3. 转换接口的使用
接口配置以XML文件描述,XSD文件定义节点结构,节点分为转换接口和扩展节点,转换节点用于做数据转换处理,可拥有多个子节点,一般先执行父节点,后执行子节点,执行方式有顺序、选择、循环,子节点如果出现异常,父节点可捕获做处理;扩展节点用于对接口配置或转换节点扩展使用;
3.1 接口配置说明
转换定义文件:有XSD文件表示,定义具体转换节点,需描述节点名称、参数名称,参数类型和转换处理类;
<!--MD5加密-->
<xsd:element name="md5">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="nestedConverterType">
<!--MD5处理类-->
<xsd:attribute name="clazz" type="xsd:string"
fixed="com.nnk.ecsys.interfaceConverter.converter.Md5Converter"/>
<!--加密字符串-->
<xsd:attribute name="value" type="paramType" use="required"/>
<!--编码类型-->
<xsd:attribute name="charsetType" type="paramType" use="optional"/>
<!--大小写类型-->
<xsd:attribute name="caseType" type="caseType" use="optional"
default="LowerCase"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
转换节点:由XML节点表示,做具体的转换处理,可有多个输入参数和一个输出参数,输入参数由具体转换节点定义,属性name表示输出参数,转换结果以name值作为名称存入【上下文变量】;
输入参数:值可直接用字符串表示,如需引用【上下文变量】则使用${变量名}表示,还可以相互组合, 如:value="${参数2} AAA ${参数2}",如需要引用对象的参数,则使用${变量名.参数名}表示,基本用法和EL表达式类似;
扩展节点:可自定义扩展节点,针对接口配置或转换节点做处理,现有扩展节点,import可导入其他接口配置,loadProperty可加载properties文件变量;
示例:
interface-trade.xml
<?xml version="1.0" encoding="UTF-8"?>
<converter xmlns="http://www.007ka.com/schema/converter"
xmlns:extend="http://www.007ka.com/schema/converter/extend"
xmlns:interface="http://www.007ka.com/schema/converter/interface">
<!--导入配置文件-->
<extend:loadProperty location="properties/common.properties"/>
<!--定义请求对象-->
<getRequest name="request"/>
<!--组装请求报文-->
<interface:tryException errorCode="CEC_FAILED" errorMsg="请求报文组装异常">
<!--转换参数-->
<set name="orderReq" value="${request.reqeustParam}" />
<interface:mapget name="cardType" key="${orderReq.providerId}" mappingName="providerMap"/>
<!--组装协议-->
<set name="orderInfo"
value="${nnk_merId}|${orderReq.orderId}|${cardType}|${orderReq.unitCount}|"/>
<interface:md5 name="sign" value="${orderInfo}|${inter_channelKey}"
charsetType="${inter_charset}"/>
<interface:newInstance name="httpParam" value="java.util.HashMap">
<set name="httpParam.Orderinfo" value="${orderInfo}"/>
<set name="httpParam.Sign" value="${sign}"/>
</interface:newInstance>
</interface:tryException>
<!--调用http接口-->
<interface:tryException errorCode="CEC_UNKNOWN" errorMsg="网络异常">
<interface:http name="httpResponse" url="${inter_interUrl}"
paramMap="${httpParam}" charsetType="${inter_charset}"/>
</interface:tryException>
<!--解析响应报文-->
<interface:tryException errorCode="CEC_UNKNOWN" errorMsg="响应报文解析异常">
<!--解析报文-->
<interface:xmlToMap name="mapRes" value="${httpResponse}" />
<set name="mapRes" value="${mapRes.root}"/>
<!--协议验签-->
<interface:verifyMd5 charsetType="${inter_charset}" sign="${mapRes.Sign}"
value="${mapRes.MerID}|${mapRes.OrderID}|${mapRes.TranStat}|${inter_channelKey}"/>
<!--组装响应对象-->
<interface:newInstance name="orderRes"
value="com.nnk.ecsys.database.mapper.order.entity.ExternOrderOrderInfo">
<set name="orderRes.orderId" value="${mapRes.OrderID}"/>
<set name="orderRes.partnerOrderId" value="${mapRes.TranOrder}"/>
<set name="orderRes.partnerOrderReceiveErrorCode" value="${mapRes.TranStat}"/>
<set name="orderRes.partnerOrderReceiveErrorMsg" value="${mapRes.TranInfo}"/>
</interface:newInstance>
</interface:tryException>
<!--返回响应结果-->
<setResponse value="${orderRes}"/>
</converter>
TestMain.java
//接口配置资源
Resource resource = ResourceUtils.getResource("interface-trade.xml");
//转换构建器
ConverterStackerBuilder converterStackerBuilder = new DefaultConverterStackerBuilder();
//创建转换器
ConverterStacker converterStacker = converterStackerBuilder.buildConverterStacker(resource);
//执行转换
Order.ExternOrderInfo tradeRequest= Order.ExternOrderInfo.getDefaultInstance();
ExternOrderOrderInfo tradeResponse = (ExternOrderOrderInfo) converterStacker.invoke(tradeRequest);
4. 转换接口的实现
4.1 配置文件的解析与扩展
DefinitionReader // 配置读取类,读取解析接口配置文件
NamespaceHandler // 命名空间处理类,包含命名空间内每个节点的解析对象
NamespaceHandlerResolver // 命名空间管理类,管理每个命名空间的XSD文件路径和处理类
DefinitionParser // 节点解析类,解析具体的转换节点或扩展节点
ConverterDefinition // 转换节点定义信息类,包含返回值名称,参数列表,转换执行类名,子节点列表等信息
ConverterDefinitionContext // 转换定义上下文信息类,包含转换节点结构信息,常量参数对象等信息
处理流程:
1.DefinitionReader读取接口配置,递归解析节点信息,
2.根据节点命名空间调用NamespaceHandlerResolver获取指定NamespaceHandler处理
2.NamespaceHandler内部根据节点名称获取对应的DefinitionReader
3.DefinitionReader解析完成后把ConverterDefinition返回DefinitionReader,并存入ConverterDefinitionContext中
4.递归解析完成后最后得到ConverterDefinitionContext对象
扩展说明:
1.添加扩展节点解析类DefinitionParser,解析方法可获取ConverterDefinitionContext对象,可自定义转换节点,或对接口配置或转换节点做扩展处理;
2.添加命名空间处理类NamespaceHandler,描述节点名称与DefinitionParser解析类对应关系
3.添加扩展配置信息,在目录META-INF.converter下添加扩展配置:XSD文件、handlers.properties、schemas.properties,在NamespaceHandlerResolver对象初始化的时候,会读取运行环境下所有Jar下此目录的扩展文件;
XSD文件:定义扩展节点结构信息,以及指明命名空间名称
handlers.properties:描述命名空间与之对应的NamespaceHandler类
schemas.properties:描述命名空间与之对应的XSD文件路径
4.2 转换器的构建
ConverterDefinitionContext // 转换定义上下文信息类,包含转换节点结构信息,常量参数对象等信息
ConverterStacker // 转换器,执行具体的转换操作
Converter // 转换节点执行类
ConverterStackerBuilder // 转换器构建类
处理流程:
1.通过DefinitionReader读取接口配置,得到ConverterDefinitionContext;
2.ConverterStackerBuilder递归遍历ConverterDefinition,通过ConverterDefinition实例化Converter对象;
3.在Converter实例化过程,将会把Converter参数列表中存在的部分常量引用, 根据ConverterDefinitionContext常量列表设置为具体的值;
4.遍历完成后,每个Converter对象会有一个父节点喝多个子节点,形成树形架构的Converter集合;
5.最后使用Converter集合构建ConverterStacker对象;
4.3 转换器执行过程
ConverterStacker // 转换器,执行具体的转换操作
Converter // 转换节点执行类
ConverterRequest // 转换请求对象,作为执行过程中变量存储对象
处理流程:
1.接受转换请求,组装ConverterRequest;
2.执行开始,把根转换节点倒序放入执行栈中,然后循环出栈执行Converter;
3.在执行的过程中,根据ConverterRequest变量集合,实例化Converter的参数列表,如过程中存在未实例化的参数,将会抛出异常;
4.参数列表实例化完成后, 把变量列表传给Converter执行转换方法,完成后把结果以name为变量名把结果存入ConverterRequest;
5.转换执行完成后,获取当前Converter的后续节点倒序存入执行栈中,注意:获取后续节点的逻辑将由Converter本身实现,超类默认实现是获取子节点,但如果像选择、循环之类的结构,此方法逻辑将会重写;
6.如在Converter执行过程中出现异常,则会进入异常捕获处理分支,详情看代码;
7.循环执行到执行栈为空时,返回结果对象
4.4 上下文变量管理
ConverterRequest // 转换器请求对象
ConverterDefinitionContext // 转换定义上下文信息类,包含转换节点结构信息,常量参数对象等信息
AssemblyParam // 装配参数对象
处理流程:
详情看代码