Spring MVC : 工具 ResponseStatusExceptionResolver

本文深入探讨SpringMVC中的ResponseStatusExceptionResolver,一种内置的异常处理机制,详细讲解其工作原理,包括如何解析异常状态和原因,以及如何利用MessageSource进行国际化消息处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

ResponseStatusExceptionResolverSpring MVC内置实现的一个HandlerExceptionResolver,其主要功能如下 :

  1. 如果所发生异常是ResponseStatusException,则从中解析status,reason,然后调用response.sendError;
  2. 否则如果所发生异常上使用了注解@ResponseStatus,则从中解析status,reason,然后调用response.sendError;
  3. 否则如果所发生异常的cause也是一个异常,则对其递归执行该流程;
    以上主要功能主要体现在其方法#doResolveException中。

如果ResponseStatusExceptionResolver能够处理某个异常,最终调用了response.sendError,则可以说ResponseStatusExceptionResolver处理完了这个异常,它会返回一个空ModelAndView对象,调用者得到此信息就不用再继续处理该异常了,通常这种情况下Servlet容器会跳转到一个错误页面展示该status,reason错误信息。

如果ResponseStatusExceptionResolver不能处理指定异常,则方法#doResolveException会返回null,表示调用者需要用其他手段继续处理该异常。

另外,ResponseStatusExceptionResolver实现了接口MessageSourceAware,也就是说它会接收一个MessageSource用于解析reason对应的消息,这也是一种国际化/本地化消息处理的一种体现。

虽然ResponseStatusExceptionResolver的核心逻辑方法是#doResolveException,但是HandlerExceptionResolver约定的它对外提供服务的功能方法是#resolveException,此方法实现在基类AbstractHandlerExceptionResolver中 :

// AbstractHandlerExceptionResolver 代码片段
	@Override
	@Nullable
	public ModelAndView resolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

       // 对缺省使用的  ResponseStatusExceptionResolver 实例来讲,shouldApplyTo 方法可以被认为总是返回 true
		if (shouldApplyTo(request, handler)) {
          // 对响应对象做一些准备工作,主要是根据是否要禁止缓存的指令设置相应的头部
			prepareResponse(ex, response);
          // 这里才是调用  ResponseStatusExceptionResolver 所实现的核心逻辑 
			ModelAndView result = doResolveException(request, response, handler, ex);
			if (result != null) {
				// Print warn message when warn logger is not enabled...
				if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
					logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
				}
				// warnLogger with full stack trace (requires explicit config)
				logException(ex, request);
			}
			return result;
		}
		else {
			return null;
		}
	}

关于应用

1.引入

ResponseStatusExceptionResolverSpring MVC缺省会启用的一个HandlerExceptionResolver,关于该启用逻辑,可以参考Spring MVC缺省配置机制WebMvcConfigurationSupportbean定义方法handlerExceptionResolver对方法#addDefaultHandlerExceptionResolvers的调用。

2.使用

WebMvcConfigurationSupport所定义的bean handlerExceptionResolver实现类其实是HandlerExceptionResolverComposite,它是多个HandlerExceptionResolver对象的组合,缺省创建的ResponseStatusExceptionResolver就在其中,这个HandlerExceptionResolverComposite bean会被DispatcherServlet#processHandlerException用于解析异常,HandlerExceptionResolverComposite解析异常的过程如下 :

// HandlerExceptionResolverComposite 代码片段
	@Override
	@Nullable
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
			@Nullable Object handler,Exception ex) {

		if (this.resolvers != null) {
           //  ResponseStatusExceptionResolver 就是 this.resolvers 一员,
           // 它在下面的for循环中会被使用到
			for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
				ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
				if (mav != null) {
					return mav;
				}
			}
		}
		return null;
	}

源代码

源代码版本 : spring-webmvc-5.1.5.RELEASE

package org.springframework.web.servlet.mvc.annotation;

// 省略 import 行


public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver 
        implements MessageSourceAware {

	@Nullable
	private MessageSource messageSource;


    // MessageSourceAware 接口定义的方法,
    // 会在当前 bean 创建的合适时机被容器设置该属性
	@Override
	public void setMessageSource(MessageSource messageSource) {
		this.messageSource = messageSource;
	}


	@Override
	@Nullable
	protected ModelAndView doResolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

		try {
          //第1种情况处理 : 异常自身就是 ResponseStatusException 异常的情况 
			if (ex instanceof ResponseStatusException) {
				return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
			}

          //第2种情况处理 : 尝试从异常上获取注解 @ResponseStatus 信息  
			ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(
				ex.getClass(), ResponseStatus.class);
			if (status != null) {
				return resolveResponseStatus(status, request, response, handler, ex);
			}

          //第3种情况处理 :  异常的 cause 还是异常,递归调用该方法进行处理
			if (ex.getCause() instanceof Exception) {
				return doResolveException(request, response, handler, (Exception) ex.getCause());
			}
		}
		catch (Exception resolveEx) {
			if (logger.isWarnEnabled()) {
				logger.warn("Failure while trying to resolve exception [" 
					+ ex.getClass().getName() + "]", resolveEx);
			}
		}
        
       // 通过null告诉调用方没能处理的了该异常
		return null;
	}

	/**
	 * Template method that handles the {@link ResponseStatus @ResponseStatus} annotation.
	 * <p>The default implementation delegates to {@link #applyStatusAndReason}
	 * with the status code and reason from the annotation.
	 * @param responseStatus the {@code @ResponseStatus} annotation
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler the executed handler, or {@code null} if none chosen at the
	 * time of the exception, e.g. if multipart resolution failed
	 * @param ex the exception
	 * @return an empty ModelAndView, i.e. exception resolved
	 */
	protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
			HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {

		int statusCode = responseStatus.code().value();
		String reason = responseStatus.reason();
		return applyStatusAndReason(statusCode, reason, response);
	}

	/**
	 * Template method that handles an {@link ResponseStatusException}.
	 * <p>The default implementation delegates to {@link #applyStatusAndReason}
	 * with the status code and reason from the exception.
	 * @param ex the exception
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler the executed handler, or {@code null} if none chosen at the
	 * time of the exception, e.g. if multipart resolution failed
	 * @return an empty ModelAndView, i.e. exception resolved
	 * @since 5.0
	 */
	protected ModelAndView resolveResponseStatusException(ResponseStatusException ex,
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) 
			throws Exception {

		int statusCode = ex.getStatus().value();
		String reason = ex.getReason();
		return applyStatusAndReason(statusCode, reason, response);
	}

	/**
	 * Apply the resolved status code and reason to the response.
	 * <p>The default implementation sends a response error using
	 * {@link HttpServletResponse#sendError(int)} or
	 * {@link HttpServletResponse#sendError(int, String)} if there is a reason
	 * and then returns an empty ModelAndView.
	 * @param statusCode the HTTP status code
	 * @param reason the associated reason (may be {@code null} or empty)
	 * @param response current HTTP response
	 * @since 5.0
	 */
	protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, 
			HttpServletResponse response)
			throws IOException {

		if (!StringUtils.hasLength(reason)) {
			// reason 为空的情况 
			response.sendError(statusCode);
		}
		else {
			// reason 不为空的情况
			 // 如果也设置了消息源 this.messageSource, 尝试从其中解析消息         
			String resolvedReason = (this.messageSource != null ?
					this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
					reason);
			response.sendError(statusCode, resolvedReason);
		}
        
		// 返回一个空 ModelAndView 对象,告知调用方请求已经被处理完,使用缺省视图解析和渲染机制 
		return new ModelAndView();
	}

}

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值