通用轉換接口設計
目錄
- 轉換接口的定義
- 轉換接口的作用
- 轉換接口的使用
- 轉換接口的實現
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 // 裝配參數對象
處理流程:
詳情看代碼