不同的 HandlerExceptionResolver 之间的执行顺序

AUTHOR | nicechi
类别 | Spring MVC
发表 | 2020-08-23 13:20:01
更新 | 2020-08-24 08:32:04

Spring MVC 默认使用 ExceptionHandlerExceptionResolver 、 ResponseStatusExceptionResolver 以及 DefaultHandlerExceptionResolver 这三种的 HandlerExceptionResolver 对 Spring MVC 中抛出的异常进行集中处理

ExceptionHandlerExceptionResolver

ExceptionHandlerExceptionResolver 默认情况下第一个执行。

ExceptionHandlerExceptionResolver 与 @ExceptionHandler 一起配合来处理异常。也就是说,当 Spring MVC 抛出了一个异常的时候,默认情况下首先会把这个异常交给 ExceptionHandlerExceptionResolver 进行处理,而 ExceptionHandlerExceptionResolver 就会去找是否有能够处理这个异常的 @ExceptionHandler,如果没有的话,就会把异常交给下一个的 HandlerExceptionResolver 进行处理。

其实在 Spring MVC 3.2 之前,默认注册的是 AnnotationMethodHandlerExceptionResolver,而不是 ExceptionHandlerExceptionResolver,但两者都是用来跟 @ExceptionHandler 一起配合使用的。

ResponseStatusExceptionResolver 

ResponseStatusExceptionResolver 默认情况下第二个执行。

ResponseStatusExceptionResolver 与 @ResponseStatus 一起配合来处理异常。也就是说,当 Spring MVC 把异常交给 ResponseStatusExceptionResolver 处理的话,它会去查看这个异常是否有使用了 @ResponseStatus ,如果有使用了 @ResponseStatus 的话,就按照 @ResponseStatus 所设置的 code 以及 reason 属性将错误信息发送给客户端,如果这个异常没有使用 @ResponseStatus 的话,就会把异常交给下一个的 HandlerExceptionResolver 进行处理。

DefaultHandlerExceptionResolver 

DefaultHandlerExceptionResolver 默认情况下第三个执行。

DefaultHandlerExceptionResolver 使用了一个映射表,这个映射表主要是用来映射 Spring MVC 内部定义的异常与对应的 HTTP 状态码。

如果当前的异常在映射表中有对应的状态码的话,就会以这个状态码来返回给客户端,如果映射表中不存在相对应的状态码的话,就会将这个异常交给下一个的 HandlerExceptionResolver 进行处理。

异常与对应的状态码

HandlerExceptionResolverComposite

其实 ExceptionHandlerExceptionResolver 、ResponseStatusExceptionResolver 与 DefaultHandlerExceptionResolver 其实并没有直接在 Spring 中注册,而是间接通过 HandlerExceptionResolverComposite 才得以生效的。

HandlerExceptionResolverComposite 本身就是一个 HandlerExceptionResolver,它在 WebMvcConfigurationSupport (即 @EnableWebMvc 中所 import 的配置类)中被注册成为 bean ,下面是 WebMvcConfigurationSupport 中注册 HandlerExceptionResolverComposite 的源码:

@Bean
public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
    
	List<HandlerExceptionResolver> exceptionResolvers = new ArrayList();
	this.configureHandlerExceptionResolvers(exceptionResolvers);
	if (exceptionResolvers.isEmpty()) {
		this.addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
	}

	this.extendHandlerExceptionResolvers(exceptionResolvers);
	HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
	//将 HandlerExceptionResolverComposite&nbsp;的执行顺序设成 0 ,所以 HandlerExceptionResolverComposite&nbsp;将会在所有被注册的 HandlerExceptionResolver 中第一个被执行
	composite.setOrder(0);
	//三个默认的 HandlerExceptionResolver 就包含在 exceptionResolvers 中
	composite.setExceptionResolvers(exceptionResolvers);
	
	return composite;
        
}

上面代码中的 exceptionResolvers 中就包含了 ExceptionHandlerExceptionResolver 、ResponseStatusExceptionResolver 以及 DefaultHandlerExceptionResolver 这三个默认的 HandlerExceptionResolver 。然后当  HandlerExceptionResolverComposite 执行的时候,就会去遍历这个 exceptionResolvers ,按照 ExceptionHandlerExceptionResolver ->ResponseStatusExceptionResolver ->DefaultHandlerExceptionResolver 的顺序依次取出去处理异常:

@Nullable
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
	if (this.resolvers != null) {
		Iterator var5 = this.resolvers.iterator();

		while(var5.hasNext()) {
			HandlerExceptionResolver handlerExceptionResolver = (HandlerExceptionResolver)var5.next();
			ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
 			if (mav != null) {
 				return mav;
			}
		}
	}

	return null;
	
}

当 HandlerExceptionResolverComposite 在遍历了所有三个默认的 HandlerExceptionResolver 之后,resolveException 的返回值还是为 null 的话,就会将异常交给其他自定义的 HandlerExceptionResolver 去处理。

如何设置自定义的 HandlerExceptionResolver 与 HandlerExceptionResolverComposite 之间的执行顺序

HandlerExceptionResolverComposite 实现了 Ordered 接口,而一般来说我们自定义 HandlerExceptionResovler 的时候是去继承 AbstractHandlerExceptionResolver 类,而 AbstractHandlerExceptionResolver 同样也实现了 Ordered 接口,所以说我们可以通过设置 Ordered 接口的 getOrder() 方法的返回值的方式来改变 HanderExceptionResolver 之间的执行顺序。

因为 HandlerExceptionResolverComposite 在注册时候将 order 的值设置为了 0,所以说,如果我们想让自定义的 HandlerExceptionResovler 在 HandlerExceptionResolverComposite 执行之前就执行的话,就可以将 getOrder() 的返回值设置为小于 0 的值(可以是负数):

public class TestHandlerExceptionResolver extends AbstractHandlerExceptionResolver {

    @Override
    public int getOrder() {
    	//返回值为 -1,比 0 小,所以会比 HandlerExceptionResolverComposite&nbsp;先执行
        return -1;
    }

    @Override
    protected ModelAndView doResolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {

		return null;

    }
}

 


CATEGORY

TOP