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注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Flutter 通过Clipper实现各种自定义形状的示例代码

    Flutter 通过Clipper实现各种自定义形状的示例代码

    这篇文章主要介绍了Flutter 通过Clipper实现各种自定义形状的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • 在Spring中基于Java类进行配置的完整步骤

    在Spring中基于Java类进行配置的完整步骤

    基于Java配置选项,可以编写大多数的Spring不用配置XML,下面这篇文章主要给大家介绍了关于在Spring中基于Java类进行配置的相关资料,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2018-05-05
  • Java多线程并发之线程池任务请求拦截测试实例

    Java多线程并发之线程池任务请求拦截测试实例

    这篇文章主要介绍了Java多线程并发之线程池任务请求拦截测试实例,队列中永远没有线程被加入,即使线程池已满,也不会导致被加入排队队列,实现了只有线程池存在空闲线程的时候才会接受新任务的需求,需要的朋友可以参考下
    2023-12-12
  • Java Web项目中实现文件下载功能的实例教程

    Java Web项目中实现文件下载功能的实例教程

    这篇文章主要介绍了Java Web项目中实现文件下载功能的实例教程,分别讲解了通过超链接实现下载以及通过Servlet程序实现下载的方式,需要的朋友可以参考下
    2016-05-05
  • WebSocket+Vue+SpringBoot实现语音通话的使用示例

    WebSocket+Vue+SpringBoot实现语音通话的使用示例

    本文主要介绍了WebSocket+Vue+SpringBoot实现语音通话的使用示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-11-11
  • Java中的封装、继承和多态,你真的都懂了吗

    Java中的封装、继承和多态,你真的都懂了吗

    Java中的封装、继承和多态知识点是学习java必备的基础知识,看似简单,真正理解起来还是有一定难度的,今天小编再次通过实例代码给大家讲解java 封装继承多态知识,感兴趣的朋友一起学习下吧
    2021-05-05
  • Java中文件管理系统FastDFS详解

    Java中文件管理系统FastDFS详解

    这篇文章主要介绍了Java中文件管理系统FastDFS详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • Java动态代理和反射机制详解

    Java动态代理和反射机制详解

    这篇文章主要介绍了Java动态代理和反射机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • 零基础入门学习——Spring Boot注解(一)

    零基础入门学习——Spring Boot注解(一)

    这篇文章主要介绍了Spring Boot注解学习(一)要点,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2017-05-05
  • Java 值传递和引用传递详解及实例代码

    Java 值传递和引用传递详解及实例代码

    这篇文章主要介绍了 Java 值传递和引用传递详解及实例代码的相关资料,需要的朋友可以参考下
    2017-03-03

最新评论