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的特性之一--委托.真的,请相信我,我只是在扯淡...... 场景练习 ...
随机推荐
- codeforces 789 A. Anastasia and pebbles
链接 A. Anastasia and pebbles 题意 这个人有两个口袋,有n种类型的鹅卵石,每种鹅卵石有wi个,每次可以放同一种最多k个,每次不能把不同类型的鹅卵石放进同一个口袋,但是她可以同 ...
- hive优化分享
粘贴一下我在部门中的一次hive优化的分享. 简述 hive构建在hadoop基础上,利用分布式存储,通过mr引擎实现对大数据的计算.MR会频繁地读写磁盘而且MR任务的启动成本很高.对于hive优化显 ...
- 【深入理解Java虚拟机】自动内存管理机制——内存区域划分
Java与C++之间有一堵有内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来.C/C++程序员既拥有每一个对象的所有权,同时也担负着每一个对象生 ...
- css 垂直居中方法总结
工作中遇到垂直居中问题,特此总结了一下几种方式与大家分享.本文讨论的垂直居中仅支持IE8+ 1.使用绝对定位垂直居中 HTML <div class="container"& ...
- Project Euler 23 Non-abundant sums( 整数因子和 )
题意: 完全数是指真因数之和等于自身的那些数.例如,28的真因数之和为1 + 2 + 4 + 7 + 14 = 28,因此28是一个完全数. 一个数n被称为亏数,如果它的真因数之和小于n:反之则被称为 ...
- [Noi2002]Savage
[Noi2002]Savage 数学题. 题解回去写(有个坑点) flag++ #include <cstdio> int n,m,c[25],p[29],l[29]; int exgcd ...
- mengento 数据库模型
- lpa标签传播算法解说及代码实现
package lpa; import java.util.Arrays; import java.util.HashMap; import java.util.Map; public class L ...
- 多线程程序调用fork的现象
- OpenFace Docker 使用简介
在Docker中使用openface最大的问题是数据与主机的交互,下面我介绍几种方法来实现主机与Docker容器的数据交互. 1.第一种也是最方便的一种方法是在进入容器时使用-v参数将主机的目录挂载到 ...