Spring异常解析源码分析

 2019-10-17 21:30  阅读(923)
文章分类:Spring boot

微信公众号:java二师兄[Click]
关注回复java 1024G最新java学习资料(包含大数据资料)免费领;

异常处理重要性

  1. 良好的异常处理体系,便于对程序的后期维护
  2. 当发生错误时,程序不至于崩溃,提高程序健壮性
  3. 当发生错误时,可以在短时间内找定位问题所在
  4. 当发生错误时,避免异常栈裸奔,暴露底层架构,提高项目安全性

Spring统一异常方式

  • 使用 @ ExceptionHandler 注解(缺点:异常处理的方法必须与出错的方法在同一个Controller里面,不能全局处理)

    1    // 需要捕捉的异常 2    @ExceptionHandler({ BizException.class }) 3        // Value用于设置response的状态码,例如404,200等,reason用于响应,可以是内容语句。 4    @ResponseStatus(code=HttpStatus.BAD_REQUEST,reason="bad request") 5      // 可以返回Json也可以进行跳转 6      @ResponseBody 7    public ServerResponse<?> exception(BizException e) { 8        return ServerResponse.createByErrorMessage(e.getMessage()); 9    }

  • 实现 HandlerExceptionResolver 接口

    11 @Component   2 public class GlobalExceptionResolver implements HandlerExceptionResolver{   3 4     public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,  Exception ex) {   5         //Object handler ----> HandlerMethod 也可能为null 6         // 可以通过handler进行特定处理 7        .............. 8     }   9}   

2019101710054\_1.png

  • 使用@ControllerAdvice+ @ ExceptionHandler注解

    1@ControllerAdvice 2@ResponseBody 3public class GlobalExceptionResolver { 4    @ResponseStatus(HttpStatus.BAD_REQUEST) 5    // 可以不指定特定的异常即默认拦截所有异常 6    @ExceptionHandler(HttpMessageNotReadableException.class) 7    public ServiceResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { 8        return  ServerResponse.createByErrorMessage(e.getMessage()) 9    } 10 // 其他代码省略 11}

SpringMVC异常处理源码剖析

  • 从DispatcherServlet的doDispatch方法入手

    1protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 2        try { 3            //省略请求处理代码部分 4            }catch (Exception ex) { 5                dispatchException = ex; 6            } 7           // 捕捉异常后调用processDispatchResult方法 8            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 9        } 10        finally { 11 12        } 13    }

  • processDispatchResult如何处理呢

    1private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, 2            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { 3      // 先判断异常是否为null     4      if (exception != null) { 5            if (exception instanceof ModelAndViewDefiningException) { 6                logger.debug("ModelAndViewDefiningException encountered", exception); 7                mv = ((ModelAndViewDefiningException) exception).getModelAndView(); 8            }else { 9                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); 10                mv = processHandlerException(request, response, handler, exception); 11                errorView = (mv != null); 12            } 13        } 14    }

  • processHandlerException核心代码

    1protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, 2            Object handler, Exception ex) throws Exception { 3                // 省略了不关系部分(异常解析器排序) 4        ModelAndView exMv = null; 5        for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) { 6                         // 调用resolveException方法 7                         // Spring 自带的异常处理器暂时不讲本质上和我们自定义差不多 8            exMv = handlerExceptionResolver.resolveException(request, response, handler, ex); 9            if (exMv != null) { 10                               // 只要获取ModeAndView终止处理      11                break; 12            } 13        } 14                 // 如果没获得直接抛出异常 15        throw ex; 16    }

  • Spring自带异常解析器主要接口和实现类2019101710054\_2.png

    • AbstractHandlerMethodExceptionResolver和ExceptionHandlerExceptionResolver负责解析@ExceptionHandle
    • ResponseStatusExceptionResolver解析@ResponseStatus
    • DefaultHandlerExceptionResolver按照不同的类型分别对异常进行解析
    • SimpleMappingExceptionResolver: 通过配置的异常类和view的对应关系来解析异常

拦截404

上面介绍的方法并不能拦截404,为什么要拦截404呢?首先为了产品的安全,不随便暴露后台所用的中间件,避免黑客利用中间件本身的漏洞攻击网站,另外也可以项目与用户有良好的交互。

  • 利用Spring MVC的最精确匹配原则(@requestMapping(“*)拦截的这个方法返回一个自定义的404界面)

  • 利用web容器提供的error-page

    1 2    // 也可以拦截其他错误码比如500 3    404 4    // 确保resource目录不被spring拦截 5    /resource/view/404.htm 6  

  • 重写DispatcherServlet的noHandlerFound方法

    1protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception{ 2        if(pageNotFoundLogger.isWarnEnabled()) 3            pageNotFoundLogger.warn((new StringBuilder()).append("No mapping found for HTTP request with URI [").append(getRequestUri(request)).append("] in DispatcherServlet with name '").append(getServletName()).append("'").toString()); 4        if(throwExceptionIfNoHandlerFound){ 5            throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), (new ServletServerHttpRequest(request)).getHeaders()); 6        } else{ 7            //response.sendError(404); 8            // return; 9            // 修改为 10            // 定义一个请求路径为404的Controller方法 11            response.sendRedirect(request.getContextPath() + "/404");  12        } 13    }

项目中异常处理方案

1@Component
     2public class GlobalExceptionResolver implements HandlerExceptionResolver {
     3
     4    public static final String AJAX="X-Requested-With";
     5
     6    public ModelAndView resolveException(HttpServletRequest request,
     7            HttpServletResponse response, Object handler, Exception ex) {
     8     // 区分是否是ajax请求  
     9         boolean isAjax = isAjax(request);
    10         return handleException(request,response,handler,ex,isAjax);
    11    }
    12    /**
    13     * 判断当前请求是否为异步请求.
    14     * @param request
    15     * @param response
    16     * @return boolean
    17     * @author zhaoxin
    18     * @date 2018年9月18日 上午10:59:35
    19     */
    20    private boolean isAjax(HttpServletRequest request){
    21        return StrUtil.isNotBlank(request.getHeader(AJAX));
    22    }
    23    /**
    24     * 处理异常
    25     * @return ModelAndView
    26     * @author zhaoxin
    27     * @date 2018年9月18日 上午11:52:40
    28     */
    29    private ModelAndView handleException(HttpServletRequest request,
    30            HttpServletResponse response, Object handler,
    31            Throwable ex, boolean isajax) {
    32        //异常信息记录到日志文件中
    33        LogUtil.logError("Capture global exceptions:"+ex.getMessage());
    34        LogUtil.logException(ex);
    35        //分普通请求和ajax请求分别处理
    36        if(isajax){
    37            return handleAjax(request,response,handler,ex);
    38        }else{
    39            return handlerNotAjax(request,response,handler,ex);
    40        }
    41    }
    42
    43    /**
    44     * ajax异常处理并返回.
    45     * @param request
    46     * @param response
    47     * @param handler
    48     * @param initialEx
    49     */
    50    private ModelAndView handleAjax(HttpServletRequest request,
    51            HttpServletResponse response, Object handler,Throwable initialEx){
    52        response.setHeader("Cache-Control", "no-store");
    53                // 返回JsonView
    54        ModelAndView mav = new ModelAndView(new FastJsonJsonView());
    55        mav.addObject("msg", "System exceptions please check logs");
    56        mav.addObject("status", Const.RespondeCode.ERROR.getCode());
    57        mav.addObject("success",false);
    58        return mav;
    59    }
    60    /**
    61     * 普通页面异常处理并返回.
    62     * @param request
    63     * @param response
    64     * @param handler
    65     * @param deepestException
    66     * @return
    67     */
    68    private ModelAndView handlerNotAjax(HttpServletRequest request,HttpServletResponse response, Object handler, Throwable ex) {
    69        Map<String, Object> model = new HashMap<>();
    70        model.put("message", "System exceptions please check logs");
    71        model.put("ex", ex);
    72        return new ModelAndView("common/error500", model);
    73    }
    74
    75
    76}

求关注

2019101710054\_3.png


来源:[]()

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring异常解析源码分析

相关推荐