在項目中,我們經常會使用到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,注入到需要調用發送接口的服務類中,就可以方便的實現接口信息的報文發送請求了.
