好久沒有更新文章了,總覺得心里空空的,最近由於工作的原因,沒有來的及及時更新,總感覺應該把學習到的東西做個記錄,供大家學習,也供自己復習,溫故而知新。今天趁着周末休息時間,把自己最近在公司的做的項目做一個總結。由於是剛入職不久,進公司負責的項目內容也很少,主要還是以學習為主。
一、項目介紹
某銀行移動辦公系統,簡稱移動OA,是我進公司參與的第一個項目,主要負責的是java后台接口的開發,該系統涉及iOS,android,pad三個前台頁面,后台就是為這三個客戶端提供公共的接口,根據業務需要,以json格式傳遞過來的數據在后台進行處理,然后返回到前台進行展示。我們項目組負責的是OA里面的一個小項目-督辦,該項目要實現銀行項目申報、催促、進度查看、項目再分解等一些功能。根據頁面展示需要,我完成了一下幾個接口的開發,其中包括一些常用的查找功能和圖片壓縮下載。
在介紹接口實現之前,有必要把自己學到的json解析再分析一遍,之前接觸啊到的json格式的的字符串數據量很小,但在實際項目中數據量是如此之大,面對如此大的數據量,我也曾迷茫過,怎么解析是個問題,后來在項目經理的幫助和自己的努力下,終於完成了json解析這個工具類的開發,我覺的這個類可以應用到很多項目中,只要是涉及到json的地方都可以。
json解析代碼如下:
/** * 將json轉化成map * @param json * @return * @date 2017年2月25日 * @return Map<String,Object> * @author 我心自在 */ public static Map<String, Object> json2Map(Object json){ if(json.equals("null")){ return null; } JSONObject object = JSONObject.fromObject(json); Map map = new HashMap(); Iterator it = object.keys(); while (it.hasNext()) { String key = (String)it.next(); String value = object.getString(key); if (JSONUtils.isObject(object.get(key))) { map.put(key, json2Map(value)); } else if (JSONUtils.isArray(object.get(key))) { List list = new ArrayList(); JSONArray jArray = JSONArray.fromObject(value); for (int i = 0; i < jArray.size(); i++) { if(JSONUtils.isObject(jArray.get(i)) ){ list.add(json2Map(jArray.get(i))); }else if ( JSONUtils.isArray(jArray.get(i))){ list.add(json2List(jArray.get(i))); }else { list.add(jArray.get(i)); } } map.put(key, list); } else { map.put(key, ConvertUtil.obj2Str(object.get(key))); } } return map; }
//下面是ConvertUtil中的部分代碼,下面的方法將Object轉化為String
public static String obj2Str(Object obj) {
return obj == null ? null : obj.toString();
}
下面適用於json里面帶數組的:
/** * 用於 JSON里面帶數組的 * @param json * @return */ public static Map<String, Object> jsonArray2Map(Object json){ // if(StringUtils.startsWith(json, "{") && StringUtils.endsWith(json, "}")){ // json = "["+json+"]"; // } JSONArray jsonArray = JSONArray.fromObject(json); JSONObject object = jsonArray.getJSONObject(0); Map map = new HashMap(); Iterator it = object.keys(); while (it.hasNext()) { String key = (String)it.next(); String value = object.getString(key); if (JSONUtils.isObject(object.get(key))) { map.put(key, json2Map(value)); } else if (JSONUtils.isArray(object.get(key))) { List list = new ArrayList(); JSONArray jArray = JSONArray.fromObject(value); for (int i = 0; i < jArray.size(); i++) { if(JSONUtils.isObject(jArray.get(i)) ){ list.add(json2Map(jArray.get(i))); }else if ( JSONUtils.isArray(jArray.get(i))){ list.add(json2List(jArray.get(i))); }else { list.add(jArray.get(i)); } } map.put(key, list); } else { map.put(key, ConvertUtil.obj2Str(object.get(key))); } } return map; }
二、接口的開發
2.1 接口一:左側菜單顯示
該接口主要實現的功能是:將json傳遞過來的數據到展示到前台頁面,主要是左側菜單的三個按鈕。
代碼如下:
public class LeftMenuListServlet extends HttpServlet { private static final long serialVersionUID = -169633978370129408L; private Logger logger = Logger.getLogger(this.getClass()); private SuperviseService superviseService = (SuperviseService) ApplicationContextProvider.getBean("superviseService"); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // response.setContentType("application/x-www-form-urlencoded"); response.setContentType("text/html;charset=UTF-8"); request.setCharacterEncoding("UTF-8"); // //獲取入參 String loginUserId = ConvertUtil.obj2Str(request.getSession(true).getAttribute(Current.SESSION_KEY_USER_ID)); MDC.put("user", loUserId); MDC.put("sessionid", request.getSession().getId()); MDC.put("starttime", System.currentTimeMillis()); String opCode = "code_0001_0018_0001"; MDC.put("opcode", opCode); ResultJson resultJson = new ResultJson(); try { String json = request.getParameter("json"); if (StringUtils.isBlank(json)) { resultJson.setEc(CurrencyVariable.EC_PARAMETER_EXCEPTION); resultJson.setEm(CurrencyVariable.EM_PARAMETER_EXCEPTION); } else { // 設置入參 Map jsonMap = JsonUtil.json2Map(json); Map dataMap = (Map) jsonMap.get("data"); String bottomTab = (String) dataMap.get("bottomTab"); String headerTab = (String) dataMap.get("headerTab"); Object currentUserId = loginUserId; logger.info("code_0001_0018_0002"); logger.info("\t currentUserId = " + currentUserId); resultJson = superviseService.leftMenulist(opCode, loginUserId, bottomTab,headerTab); resultJson.setOpCode(opCode); } } catch (Exception e) { logger.error("參數異常 ", e); resultJson.setEc(CurrencyVariable.EC_PARAMETER_EXCEPTION); resultJson.setEm(CurrencyVariable.EM_PARAMETER_EXCEPTION); } String newsResultJson = new GsonBuilder().serializeNulls().create().toJson(resultJson); logger.info(opCode + " newsResultJson==" + newsResultJson); response.getOutputStream().write(newsResultJson.getBytes("UTF-8")); //業務邏輯結束。。。 MDC.put("endtime", System.currentTimeMillis());//請求結束后put // logger.info("操作結果:"+resultJson.getEc()); logger.log(SeriousLogger.SERIOUS_LEVEL, "操作結果:" + resultJson.getEc()); } } //service方法如下: @Override public ResultJson leftMenulist(String opCode, String loginId, String bottomTab, String headerTab) { CloseableHttpClient httpClient = getHttpClient(); ResultJson resultJson = new ResultJson(); resultJson.setOpCode(opCode); // resultJson.setData(jsonObject.get("data");); CloseableHttpResponse httpResponse = null; HttpPost post = new HttpPost(SUPERWISE_HOST + LEFT_MENU_LIST_PATH); log.info(post.getURI()); List<NameValuePair> list = new ArrayList<NameValuePair>(); // 封裝入參 list.add(new BasicNameValuePair("bottomTab", bottomTab)); list.add(new BasicNameValuePair("loginId", loginId)); list.add(new BasicNameValuePair("headerTab", headerTab)); // 重寫ec,em Common.util(httpClient, resultJson, httpResponse, post, list); return resultJson; }
這里解釋一下opCode。該項目的設計思想是通過OpCode來訪問servlet,提高訪問速度,有利於后期項目的維護,具體的配置在web.xml中可見:
首先定義opcode使之指向要訪問的servlet如:
//左側菜單 public static final String code_0001_0001 = "/mobileoa/web/servlet/dd/LeftMListServlet.do";
然后通過反射的方式加載此類
static{ try{ Class<?> c = Class.forName("com.mobileoa.util.ForwardCtrlVariable"); Field[] fields = c.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { String m = Modifier.toString(fields[i].getModifiers()); if (m != null && m.indexOf("final") > -1 && !("map".equals(fields[i].getName()))) { map.put(fields[i].getName(), (String) fields[i].get(String.class)); } } }catch (ClassNotFoundException e){ log.error("ForwardCtrlVariable:",e); }catch (IllegalArgumentException e){ log.error("ForwardCtrlVariable:",e); }catch (IllegalAccessException e){ log.error("ForwardCtrlVariable:",e); }
獲得opcode值,從而訪問servlet,web.xml中的配置如下:
<servlet> <servlet-name>dubanLeftMenuListServlet</servlet-name> <servlet-class>com.thitect.middletier.mobileoa.web.servlet.dd.LeftMListServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ddLeftMenuListServlet</servlet-name> <url-pattern>/mobileoa/web/servlet/dd/LeftMListServlet.do</url-pattern> </servlet-mapping>
Common中util方法主要是對http請求進行了封裝,關於http請求的實現方法,參見文章:http://www.cnblogs.com/10158wsj/p/6767209.html
public class Common { private static final Logger log = Logger.getLogger(Common.class); static ResultJson util(CloseableHttpClient httpClient, ResultJson resultJson, CloseableHttpResponse httpResponse, HttpPost post, List<NameValuePair> list) { try { UrlEncodedFormEntity uefEntity = new UrlEncodedFormEntity(list, "UTF-8"); post.setEntity(uefEntity); httpResponse = httpClient.execute(post); HttpEntity entity = httpResponse.getEntity(); String s = EntityUtils.toString(entity); JSONObject jsonObject = JSONObject.fromObject(s); log.info(jsonObject); Object result = jsonObject.get("data"); resultJson.setData(result); String ec = (String) jsonObject.get("ec"); if (ec.equals("0001")) { resultJson.setEc(CurrencyVariable.EC_DUBAN_NOEXIST); resultJson.setEm(CurrencyVariable.EM_DUBAN_NOEXIST); } if (ec.equals("0002")) { resultJson.setEc(CurrencyVariable.EC_DUBAN_EXCEED); resultJson.setEm(CurrencyVariable.EM_DUBAN_EXCEED); } if (ec.equals("0003") || ec.equals("0005")) { resultJson.setEc(CurrencyVariable.EC_DUBAN_DBERROR); resultJson.setEm(CurrencyVariable.EC_DUBAN_DBERROR); } if (ec.equals("0004")) { resultJson.setEc(CurrencyVariable.EC_DUBAN_EXCEPTION); resultJson.setEm(CurrencyVariable.EC_DUBAN_EXCEPTION); } EntityUtils.consume(entity); if (ec.equals("0000")) { resultJson.setEc(CurrencyVariable.EC_SUCESS); resultJson.setEm(CurrencyVariable.EM_SUCESS); resultJson.setData(result); } } catch (Exception e) { e.printStackTrace(); resultJson.setEc(CurrencyVariable.EC_RUNTIME_EXCEPTION); resultJson.setEm(CurrencyVariable.EM_RUNTIME_EXCEPTION); } finally { Common.CloseableHttpResponse(httpResponse); Common.CloseableHttpClient(httpClient); } return resultJson; } private static void CloseableHttpResponse(CloseableHttpResponse httpResponse) { if (httpResponse != null) { try { httpResponse.close(); } catch (IOException e) { e.printStackTrace(); } } } private static void CloseableHttpClient(CloseableHttpClient client) { if (client != null) { try { client.close(); } catch (IOException e) { e.printStackTrace(); } } } }
至此,完成了第一個接口的開發,主要的實現就是這樣,后面還陸續寫了幾個這種類型接口的開發,個人認為這幾個接口大同小異,沒有必要一一羅列,如圖所示:
2.2接口二:圖片壓縮功能
督辦項目中,有一些文件信息十一圖片的形式給出的,這就要求前端打開的圖片的速度要快,用戶體驗要好,由於之前沒有做圖片壓縮,導致打開圖片事件較長且耗費流量,所以為了實現用戶體驗,有必要對圖片做一下壓縮,壓縮之后以流的方式進行下載,這個方法是寫在action中的也就是struts框架。實現代碼如下:
/** * 查看流程意見圖片 */ public void showNodeOpinionPic(){ String picURL = request.getParameter("pic_url"); log.info("流程框架頁面:ProcBaseForm_正文表單:查看流程處理意見圖片=" + picURL); String kmURL = this.getKMHttpUrl(); String url=kmURL+picURL; String fdId = getParamFormUrl(url); logger.info("fdId=" + fdId); String loginUserId = this.getUserId(); logger.info("loginUserId=" + loginUserId); InputStream ins = null; BufferedInputStream bis = null; OutputStream out = null; HttpServletResponse response = ServletActionContext.getResponse(); List filePathResultList = oaImageReduceService.getImageList(fdId, url,request); try { if (filePathResultList != null && filePathResultList.size() > 0) { // 下載 Map<String, Object> filePathMap = (Map) filePathResultList.get(0); logger.info("filePathMap===" + filePathMap); String filePath = (String) filePathMap.get("file_path"); if (!filePath.contains(File.separator) && !filePath.contains("/")) { filePath = auditDownloadService.getFileNameByAuditCode( loginUserId, filePath); } logger.info("filePath===" + filePath); if (StringUtils.isNotEmpty(filePath)) { File file = new File(filePath); if (file.exists()) { long p = 0L; long toLength = 0L; long contentLength = 0L; int rangeSwitch = 0; // 0,從頭開始的全文下載;1,從某字節開始的下載(bytes=27000-);2,從某字節開始到某字節結束的下載(ex:bytes=27000-39000) long fileLength; String rangBytes = ""; fileLength = file.length(); logger.info("fileLength Value = " + fileLength); ins = new FileInputStream(file); bis = new BufferedInputStream(ins); response.reset(); response.setHeader("Accept-Ranges", "bytes"); String range = request.getHeader("Range"); logger.info("Range's Value = " + range); if (range != null && range.trim().length() > 0 && !"null".equals(range)) { response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT); rangBytes = range.replaceAll("bytes=", ""); if (rangBytes.endsWith("-")) { // bytes=270000- rangeSwitch = 1; p = Long.parseLong(rangBytes.substring(0, rangBytes.indexOf("-"))); contentLength = fileLength - p; // 客戶端請求的是270000之后的字節(包括bytes下標索引為270000的字節) } else { // bytes=270000-320000 rangeSwitch = 2; String temp1 = rangBytes.substring(0, rangBytes.indexOf("-")); String temp2 = rangBytes.substring( rangBytes.indexOf("-") + 1, rangBytes.length()); p = Long.parseLong(temp1); toLength = Long.parseLong(temp2); contentLength = toLength - p + 1; // 客戶端請求的是270000-320000之間的字節 } } else { contentLength = fileLength; } // 如果設設置了Content-Length,則客戶端會自動進行多線程下載。如果不希望支持多線程,則不要設置這個參數。 // Content-Length: [文件的總大小] - [客戶端請求的下載的文件塊的開始字節] //response.setHeader("Content-Length",new Long(contentLength).toString()); // 斷點開始 // 響應的格式是: // Content-Range: bytes [文件塊的開始字節]-[文件的總大小 - 1]/[文件的總大小] if (rangeSwitch == 1) { String contentRange = new StringBuffer("bytes ") .append(new Long(p).toString()).append("-") .append(new Long(fileLength - 1).toString()) .append("/") .append(new Long(fileLength).toString()) .toString(); response.setHeader("Content-Range", contentRange); bis.skip(p); } else if (rangeSwitch == 2) { String contentRange = range.replace("=", " ") + "/" + new Long(fileLength).toString(); response.setHeader("Content-Range", contentRange); bis.skip(p); } else { String contentRange = new StringBuffer("bytes ") .append("0-").append(fileLength - 1) .append("/").append(fileLength).toString(); response.setHeader("Content-Range", contentRange); } String fileName = file.getName(); response.setContentType("application/octet-stream"); response.addHeader("Content-Disposition","attachment;filename=" + fileName); out = response.getOutputStream(); int n = 0; long readLength = 0; int bsize = 1024; byte[] bytes = new byte[bsize]; if (rangeSwitch == 2) { // 針對 bytes=27000-39000 的請求,從27000開始寫數據 while (readLength <= contentLength - bsize) { n = bis.read(bytes); readLength += n; out.write(bytes, 0, n); } if (readLength <= contentLength) { n = bis.read(bytes, 0,(int) (contentLength - readLength)); out.write(bytes, 0, n); } } else { while ((n = bis.read(bytes)) != -1) { out.write(bytes, 0, n); } } } else { logger.info("文件不存在!"); } } } } catch (NumberFormatException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // 業務處理結束 if (bis != null) { try { bis.close(); } catch (Exception e2) { } } if (ins != null) { try { ins.close(); } catch (Exception e2) { } } if (out != null) { try { out.flush(); out.close(); } catch (Exception e2) { } } } /** * 從url中獲取fdId */ private static String getParamFormUrl(String url) { String fdId = ""; if (StringUtils.isEmpty(url)) { fdId = null; } String[] urlArr = url.split("\\?"); if (urlArr == null || urlArr.length == 0) { fdId = null; } for (int i = 0; i < urlArr.length; i++) { if (urlArr[i].contains("FCheckbox=")) { String[] params = urlArr[i].split("\\/"); for (int j = 0; j < params.length; j++) { if (params[j].endsWith(".jpg")) { fdId = params[j].replace(".jpg", ""); } } } } return fdId; } //service部分代碼 @Override @Transactional public List getImageList(String fdId, String url, HttpServletRequest request) { logger.info("services param[fdId="+ fdId + ", url=" + url+ "]"); List markList = fileDao.findFileByExtendMark(fdId); logger.info("markList="+ markList); byte[] fileByte = null; String fileNameTemp = null; if(markList == null || markList.size() == 0){ fileByte = this.getImageFromURLNew1(url); fileNameTemp = this.getNewPicName(fileByte, url); logger.info("生成圖片名稱"+fileNameTemp); } else { fileNameTemp = (String)((Map)markList.get(0)).get("file_path"); logger.info("從數據庫獲取圖片信息:" + fileNameTemp); } List list = fileDao.findChildFileNameByFileName(fileNameTemp, "source"); logger.info("fileDao.findChildFileNameByFileName(fileNameTemp, 'source')"+list); if (list == null || list.size() == 0) { list = postfixFileConvertService.postfixConvert(fileNameTemp, CurrencyVariable.FILE_TYPE_SOURCE_TO_IMAGE, true, fileByte, null, null, request, CurrencyVariable.PORTAL, fdId); } else { list = postfixFileConvertService.postfixConvert(fileNameTemp, CurrencyVariable.FILE_TYPE_SOURCE_TO_IMAGE, true, null, null, null, request, CurrencyVariable.PORTAL, fdId); } return list; }
這段代碼基本完成了圖片壓縮功能的實現,另外通過設置http請求告訴瀏覽器以流的方式下載圖片並顯示到前台頁頁面。
三、總結
這是從學校畢業畢業工作以來,真正意義上接觸到的企業大項目,該項目主要的架構就是Spring+Struts+Hibernate,另外還有部分業務的實現用的是Servlet,其實struts本身就是一個大大的Servlet,只不過是更好的對Servlet進行了封裝,並且能夠更好的實現MVC模式。通過該項目,大大的增加了企業項目的開發經驗,熟悉了很多開發工具的使用,本項目采用的是Spring-tools-suitIDE,目的是能夠更好的使用Spring。熟悉了代碼管理工具SVN的使用,進一步熟悉了開發服務器Weblogic的使用,在linux環境下部署、開發javaWEB項目。
寫這篇博文的目的就是及時把自己的項目經驗寫出來與大家共勉,也為增加自己的開發經驗,進一步熟悉企業項目的開發流程,開發特點。