Spring中@ExceptionHandler注解的工作原理详解
作者:安迪源文
@ExceptionHandler注解工作原理
我们知道,Spring Web注解@ExceptionHandler可以用来指定处理某类异常的控制器方法,从而在这些异常发生时,会有相应的控制器方法来处理此类异常,其定义方式如下 :
/** * 此方法定义一个异常处理器,仅仅处理异常 DemoException , 它使用一个视图对象 * DemoExceptionHandlerView 来处理异常 * @param e * @return */ @ExceptionHandler(DemoException.class) public View handleDemoException(DemoException e) { return new DemoExceptionHandlerView(e); }
另外,通过@ExceptionHandler定义异常处理控制器方法,又可以分为两类 :
- 定义在某个控制类内,这种情况下,所定义的异常处理控制器方法仅仅覆盖当前控制器类内各个方法所发生的这类异常
- 结合@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注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!