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的特性之一--委托.真的,请相信我,我只是在扯淡...... 场景练习 ...
随机推荐
- JavaScript创建对象的几种 方式
//JavaScript创建对象的七种方式 //https://xxxgitone.github.io/2017/06/10/JavaScript%E5%88%9B%E5%BB%BA%E5%AF%B9 ...
- Win10怎么批量修改文件后缀名?
Win10怎么批量修改文件后缀名?一般我们都是右击重命名,但是,如果要改的文件很多的话,这样做事不行的,该怎么批量修改后缀名呢?下面我们一起来看看两种解决办法 通常我们修改文件后缀名都是右击>& ...
- gcc和g++的区别和联系
gcc和g++都是GNU(一个组织)的编译器. 1.对于.c后缀的文件,gcc把它当做是C程序:g++当做是C++程序: 2.对于.cpp后缀的文件,gcc和g++都会当做c++程序. 3.编译阶段, ...
- sass揭秘之@if,@for,@each(转载)
因为文章内含有很多sass代码,如需自己动手查看编译结果,推荐使用sassmeister这款在线编译工具,方便你阅读学习. 经过上两篇揭秘,大家心里对sass应该有了很好的认知感了,这篇文章基于前面两 ...
- 用shell编写一个三角形图案
第一种方法 #!/bin/bash read -p "请输入层数: " n for (( i=1; i<=$n;i++ ))do for (( j=n; j>=i; ...
- [luogu2081 NOI2012] 迷失游乐园 (树形期望dp 基环树)
传送门 题目描述 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩. 进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m ...
- TensorFlow 版本问题
TensorFlow各个版本均可以在GitHub上下载,之前下载配置的是0.5.0版本,运行的时候,出现很多问题,什么模块缺失attribute,函数参数问题等,修改起来让人抓狂,后来索性下载使用0. ...
- Elasticsearch---基于scroll技术滚动搜索大量数据
如果一次性要查出来比如10万条数据,那么性能会很差,此时一般会采取用scoll滚动查询,一批一批的查,直到所有数据都查询完处理完 使用scoll滚动搜索,可以先搜索一批数据,然后下次再搜索一批数据,以 ...
- elasticsearch(七)java 搜索功能Search Request的介绍与使用
目录 1,首先创建主搜索请求: 2,对主搜索请求进行参数设置 前端几节都是介绍的基于单个文档或着单个文档库的操作, 本节开始将介绍基于所有或指定的任何个数文档库的操作的api SearchReques ...
- 工具-VIM常用快捷键
使用vs code 的插件 amvim来快速编辑代码,要熟记下面的命令 由normal模式切换到insert模式的命令键 i 在光标左侧输入正文 I 在光标所在行的行首输入正文 a 在光标右侧输入正文 ...