在项目中,我们经常会使用到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接口客户端 结合策略模式实现总结的更多相关文章

  1. 用Map+函数式接口来实现策略模式

    用Map+函数式接口来实现策略模式 目前在魔都,贝壳找房是我的雇主,平时关注一些 java 领域相关的技术,希望你们能在这篇文章中找到些有用的东西.个人水平有限,如果文章有错误还请指出,在留言区一起交 ...

  2. 为什么会有Comparable与Comparator接口? 引入策略模式

    目录 引入 Comparable接口的来龙去脉 引入Comparator接口 什么是策略模式? 使用了策略模式有什么好处? 引入 大家先考虑一个场景, 有一个整形数组, 我们希望通过调用一个工具类的排 ...

  3. C++设计模式-策略模式(2)

    策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中从而使得它们可以相互替换. 策略模式使得算法可以在不影响到客户端的情况下发生变化.策略模把行为和环境分开.环境类负责维持和查询 ...

  4. [工作中的设计模式]策略模式stategy

    一.模式解析 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 策略模式的关键点为: 1.多种算法存在 2.算法继承同样的接口 ...

  5. C++设计模式——策略模式

    策略模式 在GOF的<设计模式:可复用面向对象软件的基础>一书中对策略模式是这样说的:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.该模式使得算法可独立于使用它的客户而变化 ...

  6. 设计模式原来如此-策略模式(Strategy Pattern)

    策略模式中体现了两个非常基本的面向对象设计的原则:1.封装变化的概念.2.编程中使用接口,而不是对接口的实现. 策略模式的定义:定义一组算法,将每个算法都封装起来,并使它们之间可以互换.策略模式使这些 ...

  7. ASP.NET MVC 学习笔记-2.Razor语法 ASP.NET MVC 学习笔记-1.ASP.NET MVC 基础 反射的具体应用 策略模式的具体应用 责任链模式的具体应用 ServiceStack.Redis订阅发布服务的调用 C#读取XML文件的基类实现

    ASP.NET MVC 学习笔记-2.Razor语法   1.         表达式 表达式必须跟在“@”符号之后, 2.         代码块 代码块必须位于“@{}”中,并且每行代码必须以“: ...

  8. http接口服务方结合策略模式实现总结

    在项目中,我们经常会使用到http+xml的接口,而且不仅仅的是一个,可能会有多个http的接口需要实时的交互.但是http接口的接收消息的公共部分是一样的,只有每个接口的报文解析和返回报文是不同的, ...

  9. C#委托和事件?策略模式?接口回调?还不清楚的赶紧来看我扯

    早前学习委托的时候,写过一点东西,今天带着新的思考和认知,再记点东西.这篇文章扯到设计模式中的策略模式,观察者模式,还有.NET的特性之一--委托.真的,请相信我,我只是在扯淡...... 场景练习 ...

随机推荐

  1. HashMap以及ConcurrentHashMap

    HashMap源码相关 HashMap实现原理及源码分析 总之就是这个博客,简直就是源码带逛,开心,最关键的是下面的图像 另外,自己的理解加上源码,总结如下 hash,原义散列,就是一对一: hash ...

  2. ios 编译版本 最低版本 运行版本 动态链接库

    if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) 运行环境判断: #if __IPHONE_OS_VERSION_ ...

  3. js 把json字符串转为json对象

       <input type="hidden" name="data" id="data" value='[{"name&q ...

  4. CorelDRAW 2019新品发布,行业大咖就差你了

    近日,由苏州思杰马克丁软件公司独家代理的CorelDRAW 2019将在苏州开启一场设计上的饕餮盛宴,您报名了么? 不管您是专业的设计师还是热爱设计的狂热粉丝,都将有机会参与到我们的活动中,为了这场盛 ...

  5. Juery实现选项卡

    选项卡是一种很常用的组件.比如3个选项的选项卡,比较笨的一种办法是,把3个状态写成3个独立页面,互相链接.这样做的问题也显而易见,切换的时候url会变.如果是手机端网页,加载慢一点,给人的感觉是不断的 ...

  6. 网页小技巧-360doc个人图书馆复制文字

    用过这个网站的人知道,当你像平时一样复制网页的地址时,这个网站会弹出如下的提示框: 这时候如果你没有账号,又不想注册.真的是一种很崩溃的感觉,但是除了注册登录外,就没有其他的办法了吗? 熟悉网页调试的 ...

  7. 后代children() find()的区别

    $(document).ready(function(){ $("div").children(); });只拿到div下面的子标签元素 $(document).ready(fun ...

  8. mysql 锁表查看

    information_schema.INNODB_TRX    一般锁表后查询这个表  把相关的事务执行线程kill就可以了,可以分析sql语句执行场景 ​ INNODB_LOCKS​ PROCES ...

  9. java 常用API 包装 练习

    package com.oracel.demo01; import java.util.Random; public class Swzy { public static void main(Stri ...

  10. bzoj 1293: [SCOI2009]生日礼物 问题转化 + 性质分析 + 滚动数组优化

    Description 小西有一条很长的彩带,彩带上挂着各式各样的彩珠.已知彩珠有N个,分为K种.简单的说,可以将彩带考虑为x轴,每一个彩珠有一个对应的坐标(即位置).某些坐标上可以没有彩珠,但多个彩 ...