电商平台-异常模块的设计与架构

说明:任何一个软件系统都会出现各式各样的异常与错误,我们需要根据异常的情况进行捕获与分析,改善自己的代码,让其更加的稳定的,快速的运行,那么作为一个

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那边也不用处理特殊情况,一举两得。

猜你喜欢

转载自blog.csdn.net/NotBugger/article/details/80942836