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那邊也不用處理特殊情況,一舉兩得
