Spring异常解析源码分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

微信公众号: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<error-page>
    2    // 也可以拦截其他错误码比如500
    3    <error-code>404</error-code>
    4    // 确保resource目录不被spring拦截
    5    <location>/resource/view/404.htm</location>
    6  </error-page>
  • 重写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) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring异常解析源码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏