Java生鮮電商平台-異常模塊的設計與架構
說明:任何一個軟件系統都會出現各式各樣的異常與錯誤,我們需要根據異常的情況進行捕獲與分析,改善自己的代碼,讓其更加的穩定的,快速的運行,那么作為一個
B2B的Java開源生鮮電商平台,我們的異常需要思考以下幾個維度。
1. 運行的代碼異常
說明:代碼在運行的過程中,難免出現各種異常與錯誤,我們采用Log4j進行日志的記錄。
在分層代碼解耦過程中,我們統一在Controller進行異常的捕獲與日志記錄。
相關的運行的代碼異常架構如下:
/** * (商家店鋪)商品信息列表 * @param request * @param response * @return */ @RequestMapping(value = "/goods/list", method = { RequestMethod.GET}) public JsonResult goodsList(HttpServletRequest request, HttpServletResponse response) { try { //業務邏輯處理 return new JsonResult(JsonResultCode.FAILURE, "系統錯誤,請稍后重試",""); }catch(Exception ex){ logger.error("[GoodsController][goodsList] exception :",ex); return new JsonResult(JsonResultCode.FAILURE, "系統錯誤,請稍后重試",""); } }
說明:由於spring的事物控制在service層,所以service層不抓取異常。所有的異常都在controller進行抓取,而且抓取的是任何的異常
異常的記錄采用某個類[]某個方法[] exception,這種方式,然后把所有的異常的堆棧信息進行打印出來,方便進行查看與分析,定位等
2. 全局異常
說明:對於有可能出現的全局異常,我們采用Spring的全局異常處理器,進行代碼的統一處理。
2.1,對於業務層的異常,我們采用自定義異常進行獲取
核心代碼如下:
/** * service異常,業務異常繼續於當前接口 * */ public class ServiceException extends RuntimeException { private static final long serialVersionUID = 4875141928739446984L; /** * 錯誤碼 */ protected String code; /** * 錯誤信息 */ protected String message; public ServiceException(String code, String message) { super(); this.code = code; this.message = message; } public ServiceException(String code, String message, Throwable t) { super(); this.code = code; this.message = message; } public ServiceException(String message) { super(message); this.message = message; } public ServiceException(String message, Throwable t) { super(message, t); this.message = message; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String toString() { return "ServiceException [code=" + code + ", message=" + message + "]"; }
2.2 對於全局的異常,我們采用spring的統一進行處理,只需要在代碼中進行異常的拋出即可
核心代碼如下:
/** * 全局異常處理類.對后台直接拋往前台頁面的異常進行封裝處理. */ public class ExceptionHandler extends SimpleMappingExceptionResolver { private static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class); @Override protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mv = super.doResolveException(request, response, handler, ex); String url = WebUtils.getPathWithinApplication(request); logger.error("controller error.url=" + url, ex); /* 使用response返回 */ response.setStatus(HttpStatus.OK.value()); // 設置狀態碼 response.setContentType(MediaType.APPLICATION_JSON_VALUE); // 設置ContentType response.setCharacterEncoding("UTF-8"); // 避免亂碼 response.setHeader("Cache-Control", "no-cache, must-revalidate"); JsonResult jsonResult=new JsonResult(JsonResultCode.FAILURE,"系統錯誤,請聯系管理員",""); if(ex instanceof ServiceException) { ServiceException serviceException=(ServiceException)ex; String code=serviceException.getCode(); String message=serviceException.getMessage(); jsonResult=new JsonResult(code,message,""); } try { PrintWriter printWriter = response.getWriter(); printWriter.write(JSONObject.fromObject(jsonResult).toString()); printWriter.flush(); printWriter.close(); } catch (IOException e) { logger.error("與客戶端通訊異常:" + e.getMessage(), e); } logger.error("異常:" + ex.getMessage(), ex); return mv; } }
spring中還需要加上一段這樣的配置:
<!-- 全局異常處理.-->
<bean id="exceptionHandler" class="com.netcai.buyer.exception.ExceptionHandler"/>
3. 代碼格式方面
說明:我們控制所有的請求的接口,都需要返回JsonResult對象,另外,我們規定“200”字符串表示請求成功,非“200”表示失敗
相關的代碼貼出來,請大家分享:
/** * Controller層的 json格式對象 */ public class JsonResult implements java.io.Serializable { private static final long serialVersionUID = 1L; /** * 返回的編碼 */ private String code; /** * 返回的信息 */ private String message; /*** * 返回的對象 */ private Object object; public JsonResult() { super(); } public JsonResult(String code, String message, Object object) { super(); this.code = code; this.message = message; this.object = object; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public Object getObject() { return object; } public void setObject(Object object) { this.object = object; } }
相關的JsonResultCode對象如下:
/** */ public class JsonResultCode { /**成功**/ public static final String SUCCESS="200"; /**失敗**/ public static final String FAILURE="201"; }
最終形成了一套完整的數據量的處理,另外還有特殊的情況,比如:404,500等等其他的業務請求,我們應該如何處理的?
由於我們是運行在tomcat下面的,我們在web.xml進行了配置。
代碼如下
<error-page> <exception-type>java.lang.Throwable</exception-type> <location>/error_500</location> </error-page> <error-page> <exception-type>java.lang.Exception</exception-type> <location>/error_404</location> </error-page> <error-page> <error-code>500</error-code> <location>/error_500</location> </error-page> <error-page> <error-code>501</error-code> <location>/error_500</location> </error-page> <error-page> <error-code>502</error-code> <location>/error_500</location> </error-page> <error-page> <error-code>404</error-code> <location>/error_404</location> </error-page> <error-page> <error-code>403</error-code> <location>/error_404</location> </error-page> <error-page> <error-code>400</error-code> <location>/error_404</location> </error-page>
我們把可能出現的任何情況都進行了攔截,然后交給spring來處理這種請求,最終形成一套自己的特殊異常處理
代碼如下:
/** * 錯誤統一處理 */ @RestController public class ErrorController { /** * 請求異常404 * @return * @throws Exception */ @RequestMapping(value = "/error_404", method = { RequestMethod.GET, RequestMethod.POST }) public JsonResult error_404() throws Exception { return new JsonResult(JsonResultCode.FAILURE, "404請求找不到請求", ""); } /** * 服務器異常 * @return JsonResult */ @RequestMapping(value = "/error_500", method = { RequestMethod.GET, RequestMethod.POST }) public JsonResult error_500() { return new JsonResult(JsonResultCode.FAILURE, "500服務器內部錯誤", ""); } }
總結:我們通過3種情況來進行了所有可能異常的處理,全局異常,業務代碼異常,特殊情況異常,架構封裝,統一的數據返回與處理等等,進行了完美的結合,該項目運行一年半以來,采用這種異常架構后,從沒出現過返回給app什么500錯誤或者其他亂七八糟的錯誤,依然是正確的JsonResult對象,APP那邊也不用處理特殊情況,一舉兩得