Spring中@ExceptionHandler注解的工作原理详解

 更新时间:2024年01月25日 09:44:18   作者:安迪源文  
这篇文章主要介绍了Spring中@ExceptionHandler注解的工作原理详解,Spring Web注解@ExceptionHandler可以用来指定处理某类异常的控制器方法,从而在这些异常发生时,会有相应的控制器方法来处理此类异常,需要的朋友可以参考下

@ExceptionHandler注解工作原理

我们知道,Spring Web注解@ExceptionHandler可以用来指定处理某类异常的控制器方法,从而在这些异常发生时,会有相应的控制器方法来处理此类异常,其定义方式如下 :

    /**
     * 此方法定义一个异常处理器,仅仅处理异常 DemoException , 它使用一个视图对象 
     * DemoExceptionHandlerView 来处理异常 
     * @param e
     * @return
     */
    @ExceptionHandler(DemoException.class)
    public View handleDemoException(DemoException e) {
        return new DemoExceptionHandlerView(e);
    }

另外,通过@ExceptionHandler定义异常处理控制器方法,又可以分为两类 :

  1. 定义在某个控制类内,这种情况下,所定义的异常处理控制器方法仅仅覆盖当前控制器类内各个方法所发生的这类异常
  2. 结合@ControllerAdvice使用,定义在@ControllerAdvice注解的类内,这种情况下,所定义的异常处理控制器方法可用于@ControllerAdvice注解覆盖的所有控制器类方法内所发生的这类异常

那么,在这种现象背后,Spring MVC又是如何实现的呢 ? 实际上,这主要是 ExceptionHandlerExceptionResolver在起作用 :

首先,ExceptionHandlerExceptionResolver是Spring MVC缺省被启用的一个HandlerExceptionResolver,它会被作为一个组合模式HandlerExceptionResolver bean中的一个元素进入到bean容器中。

ExceptionHandlerExceptionResolver实现了接口InitializingBean,所以它在实例化时会被初始化。该过程中,它就会搜集所有的@ControllerAdvice注解类中使用@ExceptionHandler定义的异常处理控制器方法以供随后工作时使用。

接下来,DispatcherServlet初始化时,会搜集所有HandlerExceptionResolver bean记录到自己的策略组件属性List<HandlerExceptionResolver> handlerExceptionResolvers。

然后,处理某个请求时某个异常发生了。DispatcherServlet会遍历handlerExceptionResolvers中每个HandlerExceptionResolver对象试图对该异常进行处理。

    // DispatcherServlet 代码片段,
    // 在 HandlerAdaptor 执行 Handler 之后调用,
    // 如果之前的逻辑有异常,则 exception 不为 null,
    // 如果之前的逻辑执行正常, 则 exception 为 null
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
		boolean errorView = false;
		if (exception != null) {
           // 请求处理出现了异常 
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
              // 这里视图处理该异常:
              // 1. 内部将其完全处理
              // 2. 或者返回一个用于渲染错误的数据模型和视图
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}
        // ... 省略其他代码
    }        
	@Nullable
	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
			@Nullable Object handler, Exception ex) throws Exception {
		// Success and error responses may use different content types
		request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
		// Check registered HandlerExceptionResolvers...
		ModelAndView exMv = null;
		if (this.handlerExceptionResolvers != null) {
          // 遍历`handlerExceptionResolvers`中每个`HandlerExceptionResolver`对象
          // 试图对异常 ex 进行处理
			for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
				exMv = resolver.resolveException(request, response, handler, ex);
				if (exMv != null) {
					break;
				}
			}
		}
		if (exMv != null) {
			// ...
			return exMv;
		}
		throw ex;
	}    

这里,轮到ExceptionHandlerExceptionResolver处理异常时,#resolveException最终会调用ExceptionHandlerExceptionResolver#doResolveHandlerMethodException。该处理过程主要步骤如下 :

找到能处理该异常的控制器方法 – ExceptionHandlerExceptionResolver#getExceptionHandlerMethod; 先从发生异常的控制器方法所在类查找是否存在使用注解@ExceptionHandler并能处理该异常的方法;如果找不到,从所有@ControllerAdvice注解类中查找使用注解@ExceptionHandler并能处理该异常的方法; 执行处理该异常的控制器方法处理该异常;

到此这篇关于Spring中@ExceptionHandler注解的工作原理详解的文章就介绍到这了,更多相关@ExceptionHandler注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 在java中使用dom解析xml的示例分析

    在java中使用dom解析xml的示例分析

    本篇文章介绍了,在java中使用dom解析xml的示例分析。需要的朋友参考下
    2013-05-05
  • Hibernate组件映射代码详解

    Hibernate组件映射代码详解

    这篇文章主要介绍了Hibernate组件映射代码详解,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • Spring Boot集成Sorl搜索客户端的实现代码

    Spring Boot集成Sorl搜索客户端的实现代码

    本篇文章主要介绍了Spring Boot集成Sorl搜索客户端的实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • 使用MDC实现日志链路跟踪

    使用MDC实现日志链路跟踪

    这篇文章主要介绍了使用MDC实现日志链路跟踪,在微服务环境中,我们经常使用Skywalking、CAT等去实现整体请求链路的追踪,但是这个整体运维成本高,架构复杂,我们来使用MDC通过Log来实现一个轻量级的会话事务跟踪功能,下面就来看看具体的过程吧,需要的朋友可以参考一下
    2022-01-01
  • mybatis-plus 返回部分字段的解决方式

    mybatis-plus 返回部分字段的解决方式

    这篇文章主要介绍了mybatis-plus 返回部分字段的解决方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • java微信开发API第三步 微信获取以及保存接口调用凭证

    java微信开发API第三步 微信获取以及保存接口调用凭证

    这篇文章主要为大家详细介绍了java微信开发API第二步,微信获取以及保存接口调用凭证,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • Maven编译Fatal error compiling:无效的目标发行版:11问题及解决

    Maven编译Fatal error compiling:无效的目标发行版:11问题及解决

    在Java11中编译Springboot工程时遇到问题,解决方法是在pom.xml文件中指定Maven的Java编译器版本,可以使用MavenJava编译器属性或插件,在Java9及以后的版本中,也要使用插件并设置release属性
    2024-12-12
  • SpringBoot实现PDF转图片的代码示例

    SpringBoot实现PDF转图片的代码示例

    在本文中,我们使用SpringBoot演示了如何将PDF文件转换为一张或多张图片,这些示例演示了如何使用Java编程语言与其他开源技术集成,以实现各种文件格式之间的转换,感兴趣的小伙伴跟着小编一起来看看吧
    2024-08-08
  • 详细总结Java堆栈内存、堆外内存、零拷贝浅析与代码实现

    详细总结Java堆栈内存、堆外内存、零拷贝浅析与代码实现

    零拷贝,这是个耳熟能详的名词,是开发岗面试中经常提及的问题.最近在回顾Netty的基础原理,还是把NIO中关于堆外内存的知识点过了一遍,这里就针对堆栈内存 堆外内存和零拷贝这几个概念以及相关知识做一下记录,需要的朋友可以参考下
    2021-05-05
  • 关于Mybatis中SQL节点的深入解析

    关于Mybatis中SQL节点的深入解析

    这篇文章主要给大家介绍了关于Mybatis中SQL节点的深入解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-03-03

最新评论