iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > VUE >Spring MVC异常解析器的原理是什么
  • 507
分享到

Spring MVC异常解析器的原理是什么

2024-04-02 19:04:59 507人浏览 八月长安
摘要

本篇内容主要讲解“spring mvc异常解析器的原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring MVC异常解析器的原理是什么”吧!使用介

本篇内容主要讲解“spring mvc异常解析器的原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring MVC异常解析器的原理是什么”吧!

使用介绍

一般自定义异常处理策略有两种方式

  1. 使用@ExceptionHandler注解

  2. 实现HandlerExceptionResolver接口

因为@ExceptionHandler注解的方式已经足够强大,所以我们一般也很少通过实现HandlerExceptionResolver来自定义异常处理策略。

简单介绍一下@ExceptionHandler的使用,后面会结合这些例子进行源码分析

@RestController @RequestMapping("location") public class LocationController {   @RequestMapping("getLocationInfo")  public String index() {   int sum = 10 / 0;   return "locationInfo";  }   @ExceptionHandler(RuntimeException.class)  public String processRuntimeException() {   return "LocationController -> 发生RuntimeException";  }   @ExceptionHandler(Exception.class)  public String processException() {   return "LocationController -> 发生Exception";  } }

访问如下链接,返回结果为

Http://localhost:8080/location/getLocationInfo LocationController -> 发生RuntimeException

把processRuntimeException方法注释掉以后,再次访问上面的链接,结果为

LocationController -> 发生Exception

如果在每个Controller里面都写异常解析器还是很麻烦的,能不能在一个地方统一处理异常呢?当然可以,这时候就不得不用到@RestControllerAdvice或者@ControllerAdvice

写如下的全局异常解析器

@RestControllerAdvice public class MyExceptionHandler {   @ExceptionHandler(RuntimeException.class)  public String processRuntimeException() {   return "MyExceptionHandler -> 发生RuntimeException";  }   @ExceptionHandler(Exception.class)  public String processException() {   return "MyExceptionHandler -> 发生RuntimeException";  } }

访问上面的链接,返回结果为

LocationController -> 发生Exception

我们把LocationController类的processException方法也注释掉,此时LocationController类里面已经没有被@ExceptionHandler注解标记的方法了

访问上面的链接,返回结果为

MyExceptionHandler -> 发生RuntimeException

把MyExceptionHandler中的processRuntimeException方法注释掉访问上面的链接,返回结果为

MyExceptionHandler -> 发生Exception

通过以上的例子,我们可以得出如下结论

  1. @RestControllerAdvice或者@ControllerAdvice类内的解析器的优先级低于@RequestMapping类的解析器的优先级

  2. 如果一个异常能被多个解析器所处理,则选择继承关系最近的解析器

假设BizException继承自NullPointException A方法解析BizException B方法解析NullPointException  C方法解析Exception

BizException会被A方法解析 NullPointException会被B方法解析  如果没有A方法,则BizException会被B方法解析,如果B方法也没有,则被C方法解析,不难理解哈

@RestControllerAdvice和@ControllerAdvice有什么区别呢?

名字上就可以猜出@RestControllerAdvice只是在@ControllerAdvice的基础上加了@ResponseBody注解,看一波源码也确实如此。所以@RestControllerAdvice类最终返回的是JSON,@ControllerAdvice最终返回的是视图。如果你不明白为什么加了@ResponseBody注解最终返回的内容为jsON,建议看一下返回值处理器相关的内容

源码分析

异常解析器接口定义如下

public interface HandlerExceptionResolver {   // 将异常封装为ModelAndView后返回  @Nullable  ModelAndView resolveException(    httpservletRequest request, HttpServletResponse response,     @Nullable Object handler, Exception ex);  }

Spring MVC默认的异常解析器存放在如下属性中

@Nullable private List<HandlerExceptionResolver> handlerExceptionResolvers;

Spring MVC异常解析器的原理是什么

顺序依次为

  • ExceptionHandlerExceptionResolver

  • ResponseStatusExceptionResolver

  • DefaultHandlerExceptionResolver

UML图如下

Spring MVC异常解析器的原理是什么

Order接口是用来排序的哈,Spring  MVC默认的解析器不是通过Order接口来控制顺序的,因为默认的解析器都继承自AbstractHandlerExceptionResolver,并且都没有重写getOrder方法

对Spring  MVC比较清楚的小伙伴应该都知道DispatcherServlet属性的默认实现都定义在源码包的DispatcherServlet.properties文件中,List的顺序也是按这个来的。放一部分内容

org.springframework.WEB.servlet.HandlerAdapter=     org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\  org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter  org.springframework.web.servlet.HandlerExceptionResolver=     org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\  org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\  org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionReso

接下来分析这3个默认的HandlerExceptionResolver

ExceptionHandlerExceptionResolver

ExceptionHandlerExceptionResolver用于支持@ExceptionHandler,而@ExceptionHandler应该是我们最常的,方便我们自定义异常处理策略,比通过实现HandlerExceptionResolver接口的方式简单

从AbstractHandlerMethodExceptionResolver#shouldApplyTo可以看到

@Override protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {  if (handler == null) {   // handler为空,交给父类去判断   // 默认该逻辑返回true   return super.shouldApplyTo(request, null);  }  else if (handler instanceof HandlerMethod) {   HandlerMethod handlerMethod = (HandlerMethod) handler;   handler = handlerMethod.getBean();   // 交给父类判断   return super.shouldApplyTo(request, handler);  }  else {   // 不支持   return false;  } }

只有当handler为空或者handler的类型为HandlerMethod时(@RequestMapping返回的类型为HandlerMethod)才会执行后面的异常解析逻辑。所以你通过实现Controller接口或者实现HttpRequestHandler接口定义的Handler,这个注解是不起作用的

@ExceptionHandler的处理过程主要和下面2个类有关系ExceptionHandlerExceptionResolver,ExceptionHandlerMethodResolver

用几个成员变量说一下处理过程,就不贴过多的代码了

ExceptionHandlerExceptionResolver

// 省略了继承和实现关系 public class ExceptionHandlerExceptionResolver {   @Nullable  private HandlerMethodArgumentResolverComposite argumentResolvers;   @Nullable  private HandlerMethodReturnValueHandlerComposite returnValueHandlers;   private List<HttpMessageConverter<?>> messageConverters;    // 被@RequestMapping标记的类 -> ExceptionHandlerMethodResolver  private final Map<Class<?>, ExceptionHandlerMethodResolver>   exceptionHandlerCache = new ConcurrentHashMap<>(64);   // 被@ControllerAdvice注解标记的类 -> ExceptionHandlerMethodResolver  private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver>  exceptionHandlerAdviceCache = new LinkedHashMap<>(); }

可以看到ExceptionHandlerExceptionResolver类定义了自己的参数处理器,返回值处理器,消息转换器。所以你可以通过这些组件反向知道@ExceptionHandler方法支持的参数类型

例如从如下方法可以知道,支持的参数类型为@SessionAttribute,@RequestAttribute等  如果你写个@RequestParam是肯定不会注入进来的

protected List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {  List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();   // Annotation-based argument resolution  resolvers.add(new SessionAttributeMethodArgumentResolver());  resolvers.add(new RequestAttributeMethodArgumentResolver());   // Type-based argument resolution  resolvers.add(new ServletRequestMethodArgumentResolver());  resolvers.add(new ServletResponseMethodArgumentResolver());  resolvers.add(new RedirectAttributesMethodArgumentResolver());  resolvers.add(new ModelMethodProcessor());   // Custom arguments  if (getCustomArgumentResolvers() != null) {   resolvers.addAll(getCustomArgumentResolvers());  }   return resolvers; }

最重要的4个map来了,ExceptionHandlerExceptionResolver的工作过程主要就是操作这4个map

// 省略了继承和实现关系 public class ExceptionHandlerExceptionResolver {   // 被@RequestMapping标记的类 -> ExceptionHandlerMethodResolver  private final Map<Class<?>, ExceptionHandlerMethodResolver>  exceptionHandlerCache = new ConcurrentHashMap<>(64);   // 被@ControllerAdvice注解标记的类 -> ExceptionHandlerMethodResolver  private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver>  exceptionHandlerAdviceCache = new LinkedHashMap<>();     }

exceptionHandlerCache保存了@RequestMapping对应的ExceptionHandlerMethodResolver,是在执行异常解析的过程中被赋值的

exceptionHandlerAdviceCache保存了@ControllerAdvice对应的  ExceptionHandlerMethodResolver,是在ExceptionHandlerExceptionResolver被初始化的过程中赋值的

而ExceptionHandlerMethodResolver你可以认为只是封装了一下Exception及其对应的Method

以最开始的例子演示,ExceptionHandlerExceptionResolver初始化后

Spring MVC异常解析器的原理是什么

此时exceptionHandlerCache是没有值的  访问如下链接后

http://localhost:8080/location/getLocationInfo

exceptionHandlerCache中的值如下,LocationController及其对应的ExceptionHandlerMethodResolver被放了进来追一下以下方法的执行  ExceptionHandlerExceptionResolver#doResolveHandlerMethodException  ExceptionHandlerExceptionResolver#getExceptionHandlerMethod

可以得出我们测试的结论@RestControllerAdvice或者@ControllerAdvice类内的解析器的优先级低于@RequestMapping类的解析器的优先级

总体实现也不难,从exceptionHandlerCache中能找到解析器就返回执行,找不到就从exceptionHandlerAdviceCache中找,这不是就实现了优先级了吗?

接着来看剩下的2个Map

public class ExceptionHandlerMethodResolver {    // 异常 -> 对应的处理方法  private final Map<Class<? extends Throwable>, Method>  mappedMethods = new HashMap<>(16);   // 异常 -> 对应的处理方法  // 这个是基于mappedMethods又做了一次缓存  // 为什么要再做一次缓存呢?  // 是因为根据异常类型获取处理方法的时候,一个异常可能有多个处理方法,即一个异常会从mappedMethods中查出多个处理方法  // 最后返回的是继承关系最近的异常对应的处理方法,所以在查找的时候又做了一次缓存,避免每次查mappedMethods然后取最优值  // 从exceptionLookupCache中就可以直接查到最优的处理方法  private final Map<Class<? extends Throwable>, Method>  exceptionLookupCache = new ConcurrentReferenceHashMap<>(16);   }

@ControllerAdvice的mappedMethods是在ExceptionHandlerExceptionResolver初始化的过程中赋值的

Spring MVC异常解析器的原理是什么

@RequestMapping的mappedMethods是在执行异常解析的过程中被赋值的

Spring MVC异常解析器的原理是什么

而exceptionLookupCache是在异常解析过程中,通过Exception查找Method的过程中基于mappedMethods做的缓存

为什么在查找过程中要再做一次缓存呢?

是因为根据异常类型获取处理方法的时候,一个异常可能有多个处理方法,即一个异常会从mappedMethods中查出多个处理方法,最后返回的是继承关系最近的异常对应的处理方法,所以在查找的时候又做了一次缓存,避免每次查mappedMethods然后取最优值。从exceptionLookupCache中就可以直接查到最优的处理方法

以LocationController为例,查找一次异常后,exceptionLookupCache的值如下

Spring MVC异常解析器的原理是什么

这样当再次发生ArithmeticException异常时就能从exceptionLookupCache找到对应的处理方法

ResponseStatusExceptionResolver

ResponseStatusExceptionResolver和DefaultHandlerExceptionResolver的实现都不是很难,就不进行过多的分析了

ResponseStatusExceptionResolver主要用来处理如下异常

抛出的异常类型继承自ResponseStatusException

抛出的异常类型被@ResponseStatus标记

以一个例子来演示这个处理器的功能

@ResponseStatus(HttpStatus.UNAUTHORIZED) public class UnauthorizedException extends RuntimeException { } @RestController @RequestMapping("shoppinGCar") public class ShoppingCarController {   @RequestMapping("getCarInfo")  public String index() {   throw new UnauthorizedException();  } }

访问

http://localhost:8080/shoppingCar/getCarInfo

显示如下

Spring MVC异常解析器的原理是什么

DefaultHandlerExceptionResolver

用来处理一些常见的Http异常,如

400:请求无效 405:请求方法不支持 500:内部服务器错误

执行入口

# DispatcherServlet#processDispatchResult的部分方法 // 处理过程发生了异常 if (exception != null) {  if (exception instanceof ModelAndViewDefiningException) {   logger.debug("ModelAndViewDefiningException encountered", exception);   // 直接使用异常中封装的ModelAndView作为最终的ModelAndView结果   mv = ((ModelAndViewDefiningException) exception).getModelAndView();  }  else {   // 其他异常类型,先获取解析器   Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);   // 通过异常解析器将异常解析为一个错误视图   mv = processhandlerException(request, response, handler, exception);   errorView = (mv != null);  } }

如果整个处理过程发生异常,依次调用DispatcherServlet的成员变量handlerExceptionResolvers的resolveException方法,找到第一个不为null的ModelAndView,然后返回

@Nullable private List<HandlerExceptionResolver> handlerExceptionResolvers;

到此,相信大家对“Spring MVC异常解析器的原理是什么”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

--结束END--

本文标题: Spring MVC异常解析器的原理是什么

本文链接: https://www.lsjlt.com/news/84935.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Spring MVC异常解析器的原理是什么
    本篇内容主要讲解“Spring MVC异常解析器的原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring MVC异常解析器的原理是什么”吧!使用介...
    99+
    2022-10-19
  • Spring MVC的原理是什么
    今天小编给大家分享一下Spring MVC的原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。SpringMVC是一种...
    99+
    2023-06-27
  • Spring MVC原理是什么
    这篇文章将为大家详细讲解有关Spring MVC原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。springMVC,是spring的一个子框架,当然拥有spring的特性,如依赖注入。在web模型...
    99+
    2023-06-27
  • tomcat+spring mvc原理是什么
    这篇文章主要讲解了“tomcat+spring mvc原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“tomcat+spring mvc原理是什么”吧!tomat + spring ...
    99+
    2023-06-02
  • Spring MVC的拦截器与异常处理机制是什么
    这篇文章主要介绍了Spring MVC的拦截器与异常处理机制是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring MVC的拦截器与异常处理机制是什么文章都会有所收获,下面我们一起...
    99+
    2023-06-29
  • spring全局异常处理的原理是什么
    Spring全局异常处理的原理是通过定义一个统一的异常处理器来捕获和处理应用程序中的任何异常。当应用程序发生异常时,Spring会将...
    99+
    2023-10-20
    spring
  • Spring MVC项目中的异常处理详解
    目录前言1. 基于配置的简单异常处理2. 基于注解的全局异常处理总结 前言 我们在项目的开发中,难免会遇到各种可预知的、不可预知的异常需要处理。每个过程都单独处理异常,系统...
    99+
    2022-11-13
  • 在Spring mvc中实现DispatchServlet的原理是什么
    在Spring mvc中实现DispatchServlet的原理是什么?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。在Spring中, ContextLoaderListene...
    99+
    2023-05-31
    springmvc hs dispatchservlet
  • Spring mvc中内置编码过滤器的原理是什么
    Spring mvc中内置编码过滤器的原理是什么?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。web.xml 中 添加如下配置:<filter> ...
    99+
    2023-05-31
    springmvc 滤器
  • Spring MVC项目中的异常处理怎么配置
    这篇文章主要介绍“Spring MVC项目中的异常处理怎么配置”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Spring MVC项目中的异常处理怎么配置”文章能帮助大家解决问题。...
    99+
    2023-06-30
  • Spring MVC的处理流程是什么
    本篇内容介绍了“Spring MVC的处理流程是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、曾经的王者&mdash;&am...
    99+
    2023-06-15
  • Spring Boot全局异常处理器(原理及使用详解)
    目录 1 什么是全局异常处理器 2 为什么需要全局异常 3 原理和目标 4 @ControllerAdvice注解 4.1 Advice(通知) 4.2 @ControllerAdvice结合方法型注解@ExceptionHandler,...
    99+
    2023-08-31
    spring boot spring java
  • Spring中什么是统一异常处理
    这篇文章主要介绍“Spring中什么是统一异常处理”,在日常操作中,相信很多人在Spring中什么是统一异常处理问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring中什...
    99+
    2022-10-19
  • Java内存异常原理是什么
    本篇内容主要讲解“Java内存异常原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java内存异常原理是什么”吧!1 . 对象的创建过程关于对象的创建,第一反应是new关键字,那么本文就...
    99+
    2023-06-02
  • Spring MVC能响应HTTP请求的原因是什么
    Spring MVC能响应HTTP请求的原因是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。很多Java面试官喜欢问这个问题:一个Spring MVC的项目文件里,开发人员...
    99+
    2023-06-02
  • spring注解校验原理是什么
    这篇文章主要介绍“spring注解校验原理是什么”,在日常操作中,相信很多人在spring注解校验原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”spring注解校验原理是什么”的疑惑有所帮助!接下来...
    99+
    2023-06-17
  • java异常处理机制原理是什么
    Java异常处理机制的原理是基于异常(Exception)的概念。在Java中,异常是指在程序运行过程中发生的错误或异常情况。当代码...
    99+
    2023-09-16
    java
  • spring session的原理是什么
    Spring Session是一种用于管理用户会话的框架,它通过将会话数据存储在外部存储介质中,而不是默认的内存中,来实现会话的持久...
    99+
    2023-09-21
    spring
  • spring scope的原理是什么
    Spring的Bean的作用域(scope)指定了一个Bean的实例是如何被创建和管理的。Spring框架提供了多种作用域,包括si...
    99+
    2023-08-31
    spring scope
  • ASP.NET MVC 2.0框架的原理是什么
    ASP.NET MVC 2.0框架的原理是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。使用微软VS工具开发Web应用程序主要有两种方式:一种是常用的创建ASP.NET W...
    99+
    2023-06-17
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作