Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 411|回复: 0

SpringMVC源码阅读:异常解析器

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-7-8 13:46:06 | 显示全部楼层 |阅读模式

    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

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

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-1 06:40 , Processed in 0.062867 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表