HTTP+XML接口客户端 结合策略模式实现总结
在项目中,我们经常会使用到http+xml的接口,而且不仅仅的是一个,可能会有多个http的接口需要实时的交互.但是http接口的发送消息的公共部分是一样的,只有每个接口的报文解析和返回报文是不同的,此时考虑到把变化和不变化的隔离出来,采取用策略模式,把公共的部分代码抽取隔离出来,每个http接口的不同的处理逻辑单独自己处理,这样也方便了后期的修改和扩展,可以很方便的修改单独的接口处理逻辑和添加新的http接口到项目中使用,而不用修改以前的设计.下面就http+xml接口的发送客户端采用简单的策略模式实现:
项目背景:SSH架构
第一,实现发送客户端的基类,把接口需要的基本参数信息,抽取出来,封装公共的发送方法,封装公共的报文组合方法,以及注入不同接口消息处理的报
* 功能详细描述:HttpClient 发送报文基础类
*
* @author lilin
* @since 2015-1-7
*/
@Service
public class BaseHttpClientSender {
//注入接口的系统标示
@Value("${appCode}")
protected String appCode;
//注入权限id
@Value("${authId}")
protected String authId;
//注入需要访问服务方的地址url
@Value("${httpUrl}")
protected String url;
private Logger logger = Logger.getLogger(BaseHttpClientSender.class); private IHttpSenderHandle httpSenderHandle;
public void setHttpSenderHandle(IHttpSenderHandle httpSenderHandle) {
this.httpSenderHandle = httpSenderHandle;
} /**
* 功能描述: 发送接口
*
* @return 返回结果
* @since 2014-9-17
*/
public Map<String, Object> httpClentSender(final String msg) {
logger.info("@@HttpClient sendXml : " + msg);
HttpClient httpClient = new DefaultHttpClient();
Map<String, Object> result = new HashMap<String, Object>();
HttpPost method = new HttpPost(url);
ContentProducer cp = new ContentProducer() {
public void writeTo(OutputStream outstream) throws IOException {
Writer writer = new OutputStreamWriter(outstream, "UTF-8");
/**
* 获取请求的xml格式数据
*/
writer.write(msg);
writer.flush();
}
};
method.setEntity(new EntityTemplate(cp));
method.addHeader("Content-Type", "text/xml");
HttpResponse response = null;
try {
response = httpClient.execute(method);
} catch (ClientProtocolException e) {
logger.error("@@HttpClient Excute ERROR! ClientProtocolException:", e);
result.put(Constants.RESFLAG, Constants.RES_E);
result.put(Constants.RESMSG, "调用接口出错!");
} catch (IOException e) {
logger.error("@@HttpClient IOException!", e);
result.put(Constants.RESFLAG, Constants.RES_E);
result.put(Constants.RESMSG, "IO出错!");
}
if (response != null) {
int status = response.getStatusLine().getStatusCode();
logger.info("@@HttpClient statusCode : " + status);
if (status == HttpStatus.SC_OK) {
HttpEntity resEntity = response.getEntity();
try {
result = httpSenderHandle.handleResponseMsg(resEntity.getContent());
} catch (Exception e) {
logger.error("@@HttpClient Get ResponseBody ERROR!", e);
result.put(Constants.RESFLAG, Constants.RES_E);
result.put(Constants.RESMSG, "获取返回报文时出错!");
}
} else {
logger.info("@@HttpClient HttpStatus ERROR!");
result.put(Constants.RESFLAG, Constants.RES_E);
result.put(Constants.RESMSG, "接口返回失败!");
}
}
logger.info("@@HttpClient SUCCESS");
return result;
} /**
* 功能描述: 封装消息提醒头部
*
* @return mbfHeader
* @since 2014-9-18
* @version
*/
protected Element getMbfHeader(String serviceCode, String opertion) {
Element mbfHeader = DocumentHelper.createElement("MbfHeader");
addElementHead(mbfHeader, "ServiceCode", serviceCode);
addElementHead(mbfHeader, "Operation", opertion);
addElementHead(mbfHeader, "AppCode", appCode);
addElementHead(mbfHeader, "UId", UUID.randomUUID().toString());
addElementHead(mbfHeader, "AuthId", authId);
return mbfHeader;
} /**
* 在目标节点上面增加一个节点: <br>
* 〈在目标节点上面增加一个节点,并返回增加的节点,节点的内容根据传入的elementText定〉<br>
* 如果传入的文本是null,那么仅仅增加节点,不增加value
*
* @param targetElement
* @param elementName
* @param elementText
* @return
*/
protected Element addElement(Element targetElement, String elementName, String elementText) {
Element temp = targetElement.addElement(elementName);
if (elementText != null) {
temp.addCDATA(elementText);
}
return temp;
} /**
* 在目标节点上面增加一个节点: <br>
* 〈在目标节点上面增加一个节点,并返回增加的节点,节点的内容根据传入的elementText定〉<br>
* 如果传入的文本是null,那么仅仅增加节点,不增加value
*
* @param targetElement
* @param elementName
* @param elementText
* @return
*/
protected Element addElementHead(Element targetElement, String elementName, String elementText) {
Element temp = targetElement.addElement(elementName);
if (elementText != null) {
temp.addText(elementText);
}
return temp;
} public String getAppCode() {
return appCode;
} public void setAppCode(String appCode) {
this.appCode = appCode;
} public String getAuthId() {
return authId;
} public void setAuthId(String authId) {
this.authId = authId;
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
} }
第二,实现业务类需要注入的实际发送接口信息的子类<LowPriceApproveHttpSender> 继承自基类实现,同时注入自己需要的基本参数信息,和实际的消息处理接口实现类.
/**
* 功能详细描述:超低价审批的接口实际发送
*
* @author lilin
* @since 2014-9-17
*/
@Service
public class LowPriceApproveHttpSender extends BaseHttpClientSender { private Logger logger = Logger.getLogger(LowPriceApproveHttpSender.class); @Value("ZYCRMExamineResults")
private String serviceCode;
@Value("examineResults")
private String operation; @Resource
private IHttpSenderHandle lowPriceApproveHttpSenderHandle; @PostConstruct
public void injectHttpSenderHandle() {
super.setHttpSenderHandle(lowPriceApproveHttpSenderHandle);
} public Map<String, Object> sendCrm(PriceBaseInfo base, LowPriceApprove approve, List<LowPriceDetail> details,
User user, String nextStep) {
logger.info("LowPrice APPROVE sendCrm START");
// 获取报文
String msg = getSenderMsg(base, approve, details, user, nextStep);
// 发送
Map<String, Object> res = super.httpClentSender(msg);
return res;
} private String getSenderMsg(PriceBaseInfo base, LowPriceApprove approve, List<LowPriceDetail> details, User user,
String nextStep) { Document document = DocumentHelper.createDocument();
document.setXMLEncoding("UTF-8");
Element root = document.addElement("MbfService");
Element input1 = root.addElement("input1");
input1.add(getMbfHeader(serviceCode, operation)); Element mbfBody = root.addElement("MbfBody");
Element input = mbfBody.addElement("input"); addElement(input, "applNo", base.getBusinessCode());
addElement(input, "apprDate", base.getEndDate().split(" ")[0]); // 如果为超公司底价审批
if ("cmp".equals(approve.getBranchType())) {
String reportUnitPrice = approve.getReportUnitPrice();
addElement(input, "apprPrice", StringUtils.isEmpty(reportUnitPrice) ? approve.getSignUnitPrice()
: reportUnitPrice);
addElement(input, "apprName", StringUtils.isNotEmpty(approve.getApprovalName()) ? approve.getApprovalName()
: user.getUserName());
addElement(input, "operatorNo", user.getUserId());
} else {
addElement(input, "apprPrice", null);
addElement(input, "apprName", null);
addElement(input, "operatorNo", null);
}
addElement(input, "apprTime", base.getEndDate().split(" ")[1]);
addElement(input, "crmNo", approve.getOrderNo());
addElement(input, "personId", base.getApplierNo());
addElement(input, "projCode", approve.getProjectCode());
addElement(input, "result", "SE".equals(nextStep) ? "1" : "2"); if (CollectionUtils.isNotEmpty(details)) {
Element tables = mbfBody.addElement("tables");
for (LowPriceDetail detail : details) {
Element tQuota = tables.addElement("tQuota");
addElement(tQuota, "limitType", detail.getLimitType());
addElement(tQuota, "useValue", detail.getAssignLimit());
addElement(tQuota, "apprRemark", detail.getRemark());
}
}
return document.asXML();
}
}
第三,定义好消息处理接口类:所有的接口处理实际类 统一实现此接口,接口用于发送消息基类的注入.实际的处理类在子类中注入实现.
/**
* 发送Http接口
* @author lilin
* @since 20150918
*/
public interface IHttpSenderHandle { /**
*
* 功能描述: <br>
* 〈功能详细描述〉
*
* @param content
* @return
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
Map<String, Object> handleResponseMsg(InputStream content); }
第四,实现,每个接口需要实际的处理消息的类:用于消息发送子类的组合注入
/**
* 功能详细描述: httpClient 接口返回消息
*
* @author lilin
* @since 2014-9-17
*/
@Service
public class LowPriceApproveHttpSenderHandle implements IHttpSenderHandle { private Logger logger = Logger.getLogger(LowPriceApproveHttpSenderHandle.class); @Override
public Map<String, Object> handleResponseMsg(InputStream inputStream) { Map<String, Object> result = new HashMap<String, Object>();
SAXReader sb = new SAXReader();
Document document;
try {
document = sb.read(inputStream);
} catch (DocumentException e) {
logger.info("ERROR IN Reader InputStream:", e);
result.put(Constants.RESFLAG, Constants.RES_E);
result.put(Constants.RESMSG, "返回报文转换出错!");
return result;
}
logger.info("@@HttpClient 解析返回报文:" + document.asXML());
Element root = document.getRootElement();
Element outElement = root.element("output1");
Element mbfHeader = outElement.element("MbfHeader");
Element serviceResponse = mbfHeader.element("ServiceResponse");
Element status = serviceResponse.element("Status");
if ("COMPLETE".equals(status.getText())) {
Element bodyElement = outElement.element("MbfBody");
Element output = bodyElement.element("output");
Element reFlag = output.element("reFlag");
Element errorMessage = output.element("errorMessage"); result.put(Constants.RESFLAG, reFlag.getText());
result.put(Constants.RESMSG, errorMessage.getText());
} else {
logger.info("@@HttpClient 接口没有成功返回:" + status.getText());
} return result;
} }
到此,简单的http+xml+策略模式的实现消息的发送客户端就完成了,此时,只要在我们需要调用的服务类之中,注入我们的客户端发送子类bean,就能实时的发送xml消息交互了.
后面扩展和修改也十分的方便,不需要修改已有的设计和代码:
新增一个新的发送接口:
1,新增加发送子类,实现当前的发送基类<BaseHttpClientSender>,注入需要处理消息的方法handle类.
2,新增处理消息的handle类,实现当前的<IHttpSenderHandle>接口,
3,把新增加的子类发送类的bean,注入到需要调用发送接口的服务类中,就可以方便的实现接口信息的报文发送请求了.
HTTP+XML接口客户端 结合策略模式实现总结的更多相关文章
- 用Map+函数式接口来实现策略模式
用Map+函数式接口来实现策略模式 目前在魔都,贝壳找房是我的雇主,平时关注一些 java 领域相关的技术,希望你们能在这篇文章中找到些有用的东西.个人水平有限,如果文章有错误还请指出,在留言区一起交 ...
- 为什么会有Comparable与Comparator接口? 引入策略模式
目录 引入 Comparable接口的来龙去脉 引入Comparator接口 什么是策略模式? 使用了策略模式有什么好处? 引入 大家先考虑一个场景, 有一个整形数组, 我们希望通过调用一个工具类的排 ...
- C++设计模式-策略模式(2)
策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中从而使得它们可以相互替换. 策略模式使得算法可以在不影响到客户端的情况下发生变化.策略模把行为和环境分开.环境类负责维持和查询 ...
- [工作中的设计模式]策略模式stategy
一.模式解析 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 策略模式的关键点为: 1.多种算法存在 2.算法继承同样的接口 ...
- C++设计模式——策略模式
策略模式 在GOF的<设计模式:可复用面向对象软件的基础>一书中对策略模式是这样说的:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.该模式使得算法可独立于使用它的客户而变化 ...
- 设计模式原来如此-策略模式(Strategy Pattern)
策略模式中体现了两个非常基本的面向对象设计的原则:1.封装变化的概念.2.编程中使用接口,而不是对接口的实现. 策略模式的定义:定义一组算法,将每个算法都封装起来,并使它们之间可以互换.策略模式使这些 ...
- ASP.NET MVC 学习笔记-2.Razor语法 ASP.NET MVC 学习笔记-1.ASP.NET MVC 基础 反射的具体应用 策略模式的具体应用 责任链模式的具体应用 ServiceStack.Redis订阅发布服务的调用 C#读取XML文件的基类实现
ASP.NET MVC 学习笔记-2.Razor语法 1. 表达式 表达式必须跟在“@”符号之后, 2. 代码块 代码块必须位于“@{}”中,并且每行代码必须以“: ...
- http接口服务方结合策略模式实现总结
在项目中,我们经常会使用到http+xml的接口,而且不仅仅的是一个,可能会有多个http的接口需要实时的交互.但是http接口的接收消息的公共部分是一样的,只有每个接口的报文解析和返回报文是不同的, ...
- C#委托和事件?策略模式?接口回调?还不清楚的赶紧来看我扯
早前学习委托的时候,写过一点东西,今天带着新的思考和认知,再记点东西.这篇文章扯到设计模式中的策略模式,观察者模式,还有.NET的特性之一--委托.真的,请相信我,我只是在扯淡...... 场景练习 ...
随机推荐
- 服务端Linux机器日志查看命令
常用查看命令 ps -ef | grep java 查看当前机器运行程序: tail -100f /... 查看日志,实时的 less /- 按页查看日志,空格翻页,q退出 cat /.. | gre ...
- 原生sql的各种问题
1.nutz有方法自动根据数据库建models吗?2.select * from a a没有建相应的models怎么取结果?3.可以直接操作result,而不是在callback里面设置吗? wend ...
- Rx (Reactive Extensions)
The Reactive Extensions (Rx) is a library for composing asynchronous and event-based programs using ...
- 采用tcp协议和UDP协议实现简单的聊天功能
Date: 2019-06-19 Author: Sun 一. Python3输出带颜色字体 实现过程: 终端的字符颜色是用转义序列控制的,是文本模式下的系统显示功能,和具体的语言无关. 转义 ...
- 【数据分析学习】Pandas思维导图
点我查看原版
- Day 13迭代器生成器
迭代器 1.迭代器就是迭代的工具,迭代也可以说成是重复,并且每一次重复都是基于上一次的结果而来的,在python中一切皆对象. 2.可迭代对象:只要拥有__iter__方法的对象都是可迭代对象. 3. ...
- linux笔记常用命令
LINUX成长日记 1.本人工作实例:(将一台服务器的数据库复制到另外一台服务器上) scp -r -P 8351 /bak_mysql/sz_b2b2c201705180200.sql root@1 ...
- Centos7安装keepalived(自定义路径安装)-高级篇
0.Keepalived介绍 Keepalived是一个基于VRRP协议来实现的服务高可用方案,可以利用其来避免IP单点故障,类似的工具还有heartbeat.corosync.pacemaker.但 ...
- [luogu2052 NOI2011] 道路修建 (树形dp)
传送门 Description 在 W 星球上有 n 个国家.为了各自国家的经济发展,他们决定在各个国家 之间建设双向道路使得国家之间连通.但是每个国家的国王都很吝啬,他们只愿 意修建恰好 n – 1 ...
- zabbix监控websphere的几个监控项
首先,我要吐槽一下这个AIX系统,这该死的天杀的玩个锤子象拔蚌的系统,没有自动补齐,删除文本字符也跟linux不一样,这让用惯的linux的我各种蓝瘦. 这个问题是在项目中遇到的,由于没有接触过AIX ...