SpringMVC之异常处理解读

目录
  • 背景
  • 源码分析
    • DispatcherServlet
    • ExceptionHandlerExceptionResolver
    • ExceptionHandlerMethodResolver
  • 自定义异常处理
  • 拓展研究
  • 总结

背景

我们的代码中,总是会抛出各种异常,例如HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException,或者是自己定义的BusinessException。

这些异常,如果我们不将它们try catch进行处理,就会导致http的状态码变成非20x,并返回SpringMvc默认的异常信息格式。

场景1:业务部门对返回值进行了统一的规定,例如:{"code":200,  "message": "success", "data": {}},当发生异常的时候,并不希望http状态码变成非20x,而是保持200,通过code来表达调用是否成功,通过message来返回失败的原因。

场景2:业务部门希望发生异常的时候,返回异常信息使用的不是SpringMvc默认的格式,而是自定义自己的格式,返回更多辅助排查问题的信息。

这两种场景,都需要对未捕获的异常进行处理,这就需要利用上SpringMvc的异常处理拓展点。

接下来基于spring-webmvc-5.3.8来分析一下异常处理~

源码分析

DispatcherServlet

DispatcherServlet是SpringMvc的入口,所有请求都经过DispatcherServlet,再进入我们定义的@Controller接口中,进行处理。

接口发生异常时,也由DispatcherServlet的handlerExceptionResolvers属性所持有的HandlerExceptionResolver来处理。

在DispatcherServlet.initHandlerExceptionResolvers中,对handlerExceptionResolers进行初始化:

  • 如果DispatcherServlet.detectAllHandlerExceptionResolvers属性为true,则会获取Spring容器中所有类型为HandlerExceptionResolver的bean
  • 如果DispatcherServlet.detectAllHandlerExceptionResolvers属性为false,则会尝试获取beanName为HANDLER_EXCEPTION_RESOLVER_BEAN_NAME的HandlerExceptionResolver的bean
  • 最后,如果从Spring容器中,找不到HandlerExceptionResolver,则会采用默认策略,生成默认的异常处理

默认情况下,detectAllHandlerExceptionResolvers为true,而Spring容器里也会有三个HandlerExceptionResolver:

  • ExceptionHandlerExceptionResolver,使用用户加了@EcxceptionHandler注解的方法来处理异常。
  • ResponseStatusExceptionHandler,如果异常类型是ResponseStatusException,则根据异常中的status和reason,利用HttpServletResponse.sendError来返回异常信息。
  • DefaultHandlerExceptionResolver,当前两个解析器没有成功处理,则该处理器会处理一些指定的异常类型,例如HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException等等;通过HttpServletResponse.sendError来返回异常信息。

ExceptionHandlerExceptionResolver

异常处理器的的处理能力来自于持有的ExceptionHandlerMethodResolver对象,有两个属性持有该类型的对象:

  • Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache,缓存来自加了@Controller注解的bean中加了@ExceptionHanlder的方法,所以key是加了@Controller注解的类,value就是对应方法的ExceptionHandlerMethodResolver封装。优先于exceptionHandlerAdviceCache
  • Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache,则是缓存了@ControllerAdvice注解的类中的异常处理,key是@ControllerAdvice的对象,value是加了@ExceptionHandler的方法对应的ExceptionHandlerMethodResolver封装。

在处理异常的方法doResolveHandlerMethodException中,会调用getExceptionHandlerMethod方法,从exceptionHandlerCache和exceptionHandlerAdviceCache获得ServletInvocableHandlerMethod。如果ServletInvocableHandlerMethod不为空,则用ServletInvocableHandlerMethod.invokeAndHandle处理异常。

所以重点就是在这两个属性的内容来源!!!

exceptionHandlerCache的内容,是在解析异常的时候才被逐步初始化,类似懒加载:

handlerMethod是抛异常的@Controller注解类的方法,handlerType就是@Controller注解的类。

注意!!!由于handlerType就是@Controller注解的类,所以new出来的ExceptionHandlerMethodResolver只会处理倒该@Controller注解的类抛出的异常(exceptionHandlerCache的key是handlerType)。

exceptionHandlerAdviceCache属性的的初始在,是在afterPropertiesSet中调用initExceptionHandlerAdviceCache,会获取Spring容器中所有加了@ControllerAdvice注解的bean,如果这些bean中有加了@ExceptionHandler注解的方法,则会被放到exceptionHandlerAdviceCache中。

ExceptionHandlerMethodResolver

再分析一下ExceptionHandlerMethodResolver的能力~

在构造函数中,会根据handlerType,通过反射的方式,选择类中加了@ExceptionHandler注解的方法,将@ExceptionHandler中的异常类型作为key,方法本身作为value,设置到mappedMethods中。

而excptionLookupCache则作为根据Throwable类型→method处理方法的缓存:

  • 如果能在excptionLookupCache缓存中找到,则直接返回
  • 如果找不到,则尝试从mappedMethods中查找
  • 如果mappedMethods中也没有找到,则会返回NO_MATCHING_EXEPTION_HANDLER_METHOD,该值也会被缓存在excptionLookupCache,这样就能避免缓存击穿!!

自定义异常处理

自定义异常处理的方式有三种:

1、自定义HandlerExceptionResolver类,注册到Spring容器上下文中,这样就会被DispatcherServlet检测到,放到handlerExceptionResolvers属性中,只要优先级高于SpringMvc默认的HandlerExceptionResolver,就可以优先处理异常。

2、在@Controller的类中,添加@ExceptionHandler注解的方法。这样,该方法就会处理其所在controller类抛出的异常。

3、在@ControllerAdvice类中增加@ExceptionHandler注解的方法。@ControllerAdvice类有几个选择属性,basePackages、basePackageClasses、assignableTypes和annotations;如果设置了这几个属性,则只有匹配这些属性规则的@Controller类的异常才会被处理。默认都为空,所有类的异常都会被处理。

拓展研究

1、@ExceptionHandler注解的方法返回值可以有多种类型,研究ServletInvocableHandlerMethod是怎么处理各种返回值类型的。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • SpringMVC学习之JSON和全局异常处理详解

    目录 1.为什么要全局异常处理 2.异常处理思路 3.SpringMVC异常分类 4.综合案例 4.1 SpringMVC自带的简单异常处理器 4.2 通过HandlerExceptionResovler接口实现全局异常 4.3 使用@ControllerAdvice+@ExceptionHandler实现全局异常 4.4 响应封装类 4.4.1 创建自定义异常类BusinessException 4.4.2 创建响应枚举类JsonResponseStatus 4.4.3 创建响应封装类Json

  • SpringMVC拦截器和异常处理器使用示例超详细讲解

    目录 拦截器 1.拦截器的配置 2.拦截器的三个抽象方法 3.多个拦截器的执行顺序 异常处理器 1.基于配置的异常处理 2.基于注解的异常处理 拦截器 1.拦截器的配置 SpringMVC中的拦截器用于拦截控制器方法的执行 SpringMVC中的拦截器需要实现HandlerInterceptor public class FirstInterceptor implements HandlerInterceptor { //在控制器方法之前拦截 @Override public boolean p

  • SpringMVC基于配置的异常处理器

    目录 一.基于配置的异常处理 储存异常信息 二.基于注解的异常处理 一.基于配置的异常处理 SpringMVC 提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver. HandlerExceptionResolver接口的实现类有: DefaultHandlerExceptionResolver,这个是默认使用的处理器,之前遇到的一些异常,其实springMVC 都已经给我们处理过了. SimpleMappingExceptionResolver

  • 详解SpringMVC中的异常处理机制

    目录 开头 1.ExceptionHandlerExceptionResolver 2. demo 开头 试想一下我们一般怎么统一处理异常呢,答:切面.但抛开切面不讲,如果对每一个controller方法抛出的异常做专门处理,那么着实太费劲了,有没有更好的方法呢?当然有,就是本篇文章接下来要介绍的springmvc的异常处理机制,用到了ControllerAdvice和ExceptionHandler注解,有点切面的感觉哈哈. 1.ExceptionHandlerExceptionResolve

  • SpringMVC整合SSM实现异常处理器详解

    目录 异常处理器 项目异常处理方案 项目异常处理代码实现 根据异常分类自定义异常类 自定义异常编码(持续补充) 触发自定义异常 异常处理器 程序开发过程中不可避免的会遇到异常现象 出现异常现象的常见位置与常见诱因如下: 框架内部抛出的异常:因使用不合规导致 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时) 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等) 表现层抛出的异常:因数据收集.校验等规则导致(例如:不匹配的数据类型间导致异常) 工具类抛出的

  • SpringMVC项目异常处理机制详解

    目录 1.异常分类 2.自定义项目业务异常 3.自定义项目系统异常 4.其他异常 5.异常代码 6.异常处理器 7.异常发生 1.异常分类 通常分为三类:系统异常(SystemException),业务异常(BusinessException)和其他异常(Exception) 业务异常指由于用户的不规范操作产生的异常,如不合法的参数传入 系统异常指项目运行过程中可预计但无法避免的异常,如数据库宕机 其他异常指开发者未曾预料到的异常 2.自定义项目业务异常 public class Busines

  • SpringMVC源码解读之 HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化

     AbstractDetectingUrlHandlerMapping是通过扫描方式注册Handler,收到请求时由AbstractUrlHandlerMapping的getHandlerInternal进行分发. 共有5个子类,一个抽象类. 与SimpleUrlHandlerMapping类似,通过覆写initApplicationContext,然后调用detectHandlers进行初始化. detectHandlers通过BeanFactoryUtils扫描应用下的Object,然后预留

  • SpringMVC源码解读之HandlerMapping

    概述 对于Web开发者,MVC模型是大家再熟悉不过的了,SpringMVC中,满足条件的请求进入到负责请求分发的DispatcherServlet,DispatcherServlet根据请求url到控制器的映射(HandlerMapping中保存),HandlerMapping最终返回HandlerExecutionChain,其中包含了具体的处理对象handler(也即我们编程时写的controller)以及一系列的拦截器interceptors,此时DispatcherServlet会根据返

  • SpringMVC源码解读之HandlerMapping - AbstractUrlHandlerMapping系列request分发

    AbstractHandlerMapping实现HandlerMapping接口定的getHandler 1. 提供getHandlerInternal模板方法给子类实现      2. 如果没有获取Handler,则使用默认的defaultHandler 3. 如果handler是string类型,从context获取实例 4. 通过getHandlerExecutionChain封装handler,添加interceptor // AbstractHandlerMapping /** * L

  • SpringMVC全局异常处理的三种方式

    在 JavaEE 项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的.不可预知的异常需要处理.每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大. SpringMvc 对于异常处理这块提供了支持,通过 SpringMvc 提供的全局异常处理机制,能够将所有类型的异常处理从各个处理过程解耦出来,这样既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护. SpringMVC全局异常处理的三

  • 简单了解SpringMVC全局异常处理常用方法

    项目中,可能会抛出多个异常,我们不可以直接将异常的堆栈信息展示给用户,有两个原因: 用户体验不好 非常不安全 所以,针对异常,我们可以自定义异常处理,SpringMVC 中,针对全局异常也提供了相应的解决方案,主要是通过 @ControllerAdvice 和@ExceptionHandler 两个注解来处理的. 以上传大小超出限制为例,自定义异常,只需要提供一个异常处理类即可: @ControllerAdvice//表示这是一个增强版的 Controller,主要用来做全局数据处理 publi

  • SpringMVC统一异常处理实例代码

    一.需求 一般项目中都需要作异常处理,基于系统架构的设计考虑,使用统一的异常处理方法. 系统中异常类型有哪些? 包括预期可能发生的异常.运行时异常(RuntimeException),运行时异常不是预期会发生的. 针对预期可能发生的异常,在代码手动处理异常可以try/catch捕获,可以向上抛出. 针对运行时异常,只能通过规范代码质量.在系统测试时详细测试等排除运行时异常. 二.统一异常处理解决方案 2.1 定义异常 针对预期可能发生的异常,定义很多异常类型,这些异常类型通常继承于Excepti

  • SpringMVC统一异常处理三种方法详解

    这篇文章主要介绍了SpringMVC-统一异常处理三种方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在 Spring MVC 应用的开发中,不管是对底层数据库操作,还是业务层或控制层操作,都会不可避免地遇到各种可预知的.不可预知的异常需要处理. 如果每个过程都单独处理异常,那么系统的代码耦合度高,工作量大且不好统一,以后维护的工作量也很大. 如果能将所有类型的异常处理从各层中解耦出来,这样既保证了相关处理过程的功能单一,又实现了异常信

  • SpringMVC 异常处理机制与自定义异常处理方式

    目录 SpringMVC默认处理的几种异常 @ResponseStatus 异常处理的顺序 自定义异常类(SpringMVC的异常处理) ①:自定义异常类 ②:自定义异常处理器 ③:配置我们的异常处理器 本节介绍SpringMVC的异常处理机制 首先介绍SpringMVC默认提供了一些HTTP错误类似码的默认异常处理 如何给一个Controller自定义异常处理 如何为项目做一个全局异常处理 提到异常处理,就不得不提HandlerExceptionResolvers,我们的DispatcherS

随机推荐