zoukankan      html  css  js  c++  java
  • SpringMVC源码阅读:异常解析器

    1.前言

    SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧

    本文将通过源码(基于Spring4.3.7)分析,弄清楚SpringMVC如何完成异常解析、捕捉异常,并自定义异常和异常解析器

    2.源码分析

    进入DispatcherServlet的processDispatchResult方法

    1024行判断异常是否是ModelAndViewDefiningException类型,如果是,直接返回ModelAndView

    不是ModelAndViewDefiningException类型,则获取HandlerMethod,调用processHandlerExeception方法

    点进去1030行的processHandlerException方法,该方法根据HandlerExecutionResolvers来解析异常并选择ModelAndView

    1217行遍历HandlerExceptionResolvers,我们讲过,在<mvc:annotation-driven/>帮我们注册了默认的异常解析器

    请看AnnotationDrivenBeanDefinitionParser(解析annotation-driven的类)

    1218行调用HandlerExceptionResolver的resolveException方法,该方法被子类AbstractHandlerExceptionResolver实现

    1225行给request设置异常信息

    现在进入HandlerExceptionResolver接口resolveException方法的实现处——AbstractHandlerExceptionResolver的resolveException方法

    131行判断该异常解析器是否可以被应用到Handler

    135行为异常情况准备response,即给response添加头部

    136行调用抽象方法doResolveException,由子类实现

    进入AbstractHandlerMethodExceptionResolver的doResolveException方法

    59行调用抽象方法,被子类ExceptionHandlerExceptionResolver实现

    打开ExceptionHandlerExceptionResolver的doResolveHandlerMethodException方法

    362行获取有异常的Controller方法

    367~368行为ServletInvocableHandlerMethod设置HandlerMethodArgumentResolverComposite和HandlerMethodReturnValueComposite,用来解析参数和处理返回值

    380行调用invokeAndHandle方法处理返回值,暴露cause

    384行无cause

    3.实例

    3.1 使用@ResponseStatus自定义异常UnauthorizedException

    @ResponseStatus会被ResponseStatusExceptionResolver解析

    @ResponseStatus(code=HttpStatus.UNAUTHORIZED,reason="用户未授权")
    public class UnauthorizedException extends RuntimeException {
    
    }

    测试方法

        @RequestMapping("/unauth")
        public Map unauth() {
            throw new UnauthorizedException();
        }

    浏览器输入http://localhost:8080/springmvcdemo/error/unauth

    3.2 无注解情况

    测试方法

        @RequestMapping("/noSuchMethod")
        public Map noHandleMethod() throws NoSuchMethodException {
            throw new NoSuchMethodException();
        }

    没有@ExceptionHandler和@ResponseStatus注解则会被DefaultHandlerExceptionResolver解析

    浏览器输入http://localhost:8080/springmvcdemo/error/noSuchMethod

    3.3 @ExceptionHandler处理异常

    测试方法

    @ExceptionHandler会被ExceptionHandlerExceptionResolver解析

        @RequestMapping("/exception")
        @ResponseBody
        public Map exception() throws ClassNotFoundException {
            throw new ClassNotFoundException("class not found");
        }
    
        @RequestMapping("/nullpointer")
        @ResponseBody
        public Map nullpointer() {
            Map resultMap = new HashMap();
            String str = null;
            str.length();
            resultMap.put("strNullError",str);
            return resultMap;
        }
    
        @ExceptionHandler(RuntimeException.class)
        @ResponseBody
        public Map error(RuntimeException error, HttpServletRequest request) {
            Map resultMap = new HashMap();
            resultMap.put("param", "Runtime error");
            return resultMap;
        }
    
        @ExceptionHandler()
        @ResponseBody
        public Map error(Exception error, HttpServletRequest request, HttpServletResponse response) {
            Map resultMap = new HashMap();
            resultMap.put("param", "Exception error");
            return resultMap;
        }

    浏览器输入http://localhost:8080/springmvcdemo/error/classNotFound

    浏览器输入http://localhost:8080/springmvcdemo/error/nullpointer

    根据异常类继承关系,ClassNotFoundException离Exception更近,所以被@ExceptionHandler()的error方法解析,注解无参相当于Exception.class。

    同理,NullPointerException方法离NullPointerException“最近”,把@ExceptionHandler(NullPointerException.class)的error方法注释掉,浏览器输入http://localhost:8080/springmvcdemo/error/nullpointer,会发现

    浏览器返回RuntimeException,印证了我们的说法

    3.4 定义全局异常处理

    /**
     * @Author: 谷天乐
     * @Date: 2019/1/21 10:48
     * @Description: ExceptionHandlerMethodResolver内部找不到Controller的@ExceptionHandler注解的话,
     * 会找@ControllerAdvice中的@ExceptionHandler注解方法
     */
    @ControllerAdvice
    public class ExceptionControllerAdvice {
    
        @ExceptionHandler(Throwable.class)
        @ResponseBody
        public Map<String, Object> ajaxError(Throwable error, HttpServletRequest request, HttpServletResponse response) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("error", error.getMessage());
            map.put("result", "error");
            return map;
        }
    
    }

    浏览器输入http://localhost:8080/springmvcdemo/error/unauth

    优先级关系:@ExceptionHandler>@ControllerAdvice中的@ExceptionHandler>@ResponseStatus

    要把TestErrorController中@ExceptionHandler的方法注释掉才会有效果

    4.总结

    HandlerExceptionResolver作为异常解析器的接口,核心方法是resolveException

    AbstractHandlerExceptionResolver实现HandlerException,resolveException方法内部调用抽象方法doResolveException,该方法被子类实现;shouldApplyTo方法检查该异常解析器是否可以被应用到Handler

    AbstractHandlerMethodExceptionResolver的doResolveException内部调用抽象方法doResolveHandlerMethodException,由子类实现,返回ModelAndView,可以在视图模型里自定义错误页面;shouldApplyTo调用父类方法

    ExceptionHandlerExceptionResovler的doResolveHandlerMethodException处理异常,返回ModelAndView

    DefaultHandlerExceptionResolver的doResolveException处理默认异常

    ResponseStatusExceptionResolver的doResolveException方法处理@ResponseStatus修饰的异常

    DispatcherServlet的processHandlerException方法根据注册的HandlerExceptionResolvers选择一个ModelAndView

    DispatcherServlet的doDispatch方法调用processDispatchResult,该方法处理Handler的选择和调用的结果,processDispatchResult方法调用processHandlerException

    5.参考

    https://docs.spring.io/spring/docs/4.3.7.RELEASE/spring-framework-reference/htmlsingle/#beans-beans-conversion

    https://docs.spring.io/spring/docs/current/javadoc-api/

    https://github.com/spring-projects/spring-framework

    文中难免有不足,欢迎指正

  • 相关阅读:
    ASP.NET 4.0的ClientIDMode属性
    关于sql链接超时的问题
    Image.Url 无法使用 Server.MapPath(使用后无论如何也不显示)
    C# 中字符串转换成日期
    Linux RAID 磁盘管理
    脚本编写
    挂载一个NFS共享
    配置NFS服务
    配置多用户SMB挂载
    通过 SMB 共享目录
  • 原文地址:https://www.cnblogs.com/Java-Starter/p/10356055.html
Copyright © 2011-2022 走看看