在Spring MVC中,如果对异常不做任何处理,Spring MVC会将异常直接抛给容器。

例如下面的代码抛出了异常:

@GetMapping("e1")
public String exception() {
	int i = 1 / 0;
	return "exception";
}

浏览器页面上会显示500错误。

在这里插入图片描述

对异常的处理

@ExceptionHandler处理当前Controller的异常

@ExceptionHandler能对当前Controller中指定的异常进行处理,可以通过ModelAndView将异常信息传递给页面。

使用如下:

@GetMapping("e1")
public String exception() {
	int i = 1 / 0;
	return "exception";
}

@ExceptionHandler(value = RuntimeException.class)
public ModelAndView handlerRuntimeException(Exception exception) {
	ModelAndView mv = new ModelAndView("error");
	mv.addObject("exception", exception);
	return mv;
}

@ExceptionHandler要注意异常的优先级,如果Controller中有如下的异常处理方法,那么还是会执行handlerArithmeticException()方法,因为ArithmeticException异常更加具体:

@ExceptionHandler(value = RuntimeException.class)
public ModelAndView handlerRuntimeException(Exception exception) {
	ModelAndView mv = new ModelAndView("error");
	mv.addObject("exception", exception);
	return mv;
}

@ControllerAdvice处理全局异常

@ExceptionHandler的作用域是当前Controller,如果想对全局的异常进行处理需要使用@ControllerAdvice。

使用如下:

@ControllerAdvice
public class ExceptionHandlerControllerAdvice {

    @ExceptionHandler(value = ArithmeticException.class)
    public ModelAndView handlerArithmeticException(Exception exception) {
        ModelAndView mv = new ModelAndView("error");
        mv.addObject("exception", exception);
        return mv;
    }

}

如果Controller中使用了@ExceptionHandler处理异常,那么他的优先级比全局的异常处理高。

@ResponseStatus定制错误码和错误内容

可以使用@ResponseStatus加到异常类上,这样当这个异常抛出时,页面显示的是定制的错误消息。

异常类定义如下:

@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "用户名和密码不匹配")
public class UsernameNotMatchPasswordException extends RuntimeException {
}

在方法中抛出这个异常:

/**
	 * 使用@ResponseStatus定义错误码和错误内容
	 * @param i
	 * @return
	 */
@GetMapping("e3")
public String exception3(int i) {
	if (13 == i) {
		throw new UsernameNotMatchPasswordException();
	}
	return "success";
}

页面显示的错误消息如下:
在这里插入图片描述

使用SimpleMappingExceptionResolver处理指定异常

注入SimpleMappingExceptionResolver,并指定要处理的异常与视图。

com.morris.spring.mvc.config.MVCConfig#extendHandlerExceptionResolvers

public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
	SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
	Properties properties = new Properties();
	// 指定要处理的异常与视图
	properties.setProperty("java.lang.NullPointerException", "error");
	simpleMappingExceptionResolver.setExceptionMappings(properties);
	resolvers.add(simpleMappingExceptionResolver);

}

在Controller中抛出指定的异常:

/**
	 * 使用SimpleMappingExceptionResolver处理异常
 *
	 * @return
 */
@GetMapping("e5")
public String exception5() {
	if(true) {
		throw new NullPointerException();
	}
	return "success";
}

这样就会跳到指定的视图error.jsp页面。

自定义HandlerExceptionResolver

如果觉得SpringMVC提供的异常处理不满足需求,可以实现HandlerExceptionResolver接口自定义异常处理。

package com.morris.spring.mvc.exception;

import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyHandlerExceptionResolver extends AbstractHandlerExceptionResolver implements PriorityOrdered {
	@Override
	protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
		if(ex instanceof ArrayIndexOutOfBoundsException) {
			ModelAndView mv = new ModelAndView("error");
			mv.addObject("exception", "MyHandlerExceptionResolver");
			return mv;
		}
		return null;
	}

	/**
	 * 优先级设置高一点,否则会被@ControllerAdvice全局异常处理
	 * @return
	 */
	@Override
	public int getOrder() {
		return 1;
	}
}

总结:不管使用上面哪种异常处理方法,只能处理在请求到达了DispatcherServlet,并且出现了异常后进入processDispatchResult()方法。

下面的这两种异常场景不适用:

  1. 请求没有到达DispatcherServlet的核心流程,如在filter中抛出异常。
  2. 请求进入processDispatchResult()方法处理异常,但是在处理过程中有抛出了异常,如在@ControllerAdvice方法中抛出了异常。

源码分析

HandlerExceptionResolverComposite的调用过程

在处理controller的返回结果时,发现有异常就会调用异常处理的逻辑:

org.springframework.web.servlet.DispatcherServlet#processDispatchResult

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);
			// 异常处理
			mv = processHandlerException(request, response, handler, exception);
			errorView = (mv != null);
		}
	}
... ...

org.springframework.web.servlet.DispatcherServlet#processHandlerException

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什么时候初始化的?
			 * @see DispatcherServlet#initHandlerExceptionResolvers(org.springframework.context.ApplicationContext)
			 *
			 * @see AbstractHandlerExceptionResolver#resolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
			 * @see HandlerExceptionResolverComposite#resolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
			 */
		for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
			exMv = resolver.resolveException(request, response, handler, ex);
			if (exMv != null) {
				break;
			}
		}
	}
	if (exMv != null) {
		if (exMv.isEmpty()) {
			request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
			return null;
		}
		// We might still need view name translation for a plain error model...
		if (!exMv.hasView()) {
			String defaultViewName = getDefaultViewName(request);
			if (defaultViewName != null) {
				exMv.setViewName(defaultViewName);
			}
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Using resolved error view: " + exMv, ex);
		}
		else if (logger.isDebugEnabled()) {
			logger.debug("Using resolved error view: " + exMv);
		}
		WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
		return exMv;
	}

	throw ex;
}

handlerExceptionResolvers何时被初始化的?

org.springframework.web.servlet.DispatcherServlet#initHandlerExceptionResolvers

private void initHandlerExceptionResolvers(ApplicationContext context) {
	this.handlerExceptionResolvers = null;

	if (this.detectAllHandlerExceptionResolvers) {
		/**
			 * HandlerExceptionResolver在WebMvcConfigurationSupport中注入了
			 */
		// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
		Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
			.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
			// We keep HandlerExceptionResolvers in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
		}
	}
	else {
		try {
			HandlerExceptionResolver her =
				context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
			this.handlerExceptionResolvers = Collections.singletonList(her);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Ignore, no HandlerExceptionResolver is fine too.
		}
	}

	// Ensure we have at least some HandlerExceptionResolvers, by registering
	// default HandlerExceptionResolvers if no other resolvers are found.
	if (this.handlerExceptionResolvers == null) {
		// 默认ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver
		this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
		if (logger.isTraceEnabled()) {
			logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
						 "': using default strategies from DispatcherServlet.properties");
		}
	}
}

handlerExceptionResolvers是从在SpringMVC容器中获取所有的HandlerExceptionResolver实例,那么这些实例是什么时候注入的呢?

org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#handlerExceptionResolver

@Bean
public HandlerExceptionResolver handlerExceptionResolver(
	@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
	List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
	configureHandlerExceptionResolvers(exceptionResolvers);
	if (exceptionResolvers.isEmpty()) {
		// 添加默认的HandlerExceptionResolver
		addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
	}
	extendHandlerExceptionResolvers(exceptionResolvers);
	HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
	composite.setOrder(0);
	composite.setExceptionResolvers(exceptionResolvers);
	return composite;
}

protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,
														 ContentNegotiationManager mvcContentNegotiationManager) {

	// 添加ExceptionHandlerExceptionResolver
	ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
	exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
	exceptionHandlerResolver.setMessageConverters(getMessageConverters());
	exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
	exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
	if (jackson2Present) {
		exceptionHandlerResolver.setResponseBodyAdvice(
			Collections.singletonList(new JsonViewResponseBodyAdvice()));
	}
	if (this.applicationContext != null) {
		exceptionHandlerResolver.setApplicationContext(this.applicationContext);
	}
	exceptionHandlerResolver.afterPropertiesSet();
	exceptionResolvers.add(exceptionHandlerResolver);

	// 添加ResponseStatusExceptionResolver
	ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
	responseStatusResolver.setMessageSource(this.applicationContext);
	exceptionResolvers.add(responseStatusResolver);

	// 添加DefaultHandlerExceptionResolver
	exceptionResolvers.add(new DefaultHandlerExceptionResolver());
}

默认注册了三个HandlerExceptionResolver:

  • ExceptionHandlerExceptionResolver
  • ResponseStatusExceptionResolver
  • DefaultHandlerExceptionResolver

我们可以通过实现WebMvcConfigurer接口extendHandlerExceptionResolvers()方法注入自己的HandlerExceptionResolver,也可以通过向SpringMVC容器中注入HandlerExceptionResolver实例。

最后ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver以及通过实现WebMvcConfigurer接口的extendHandlerExceptionResolvers()方法注入的HandlerExceptionResolver都会包装到HandlerExceptionResolverComposite中。

最后异常处理都会调用HandlerExceptionResolverComposite#resolveException

org.springframework.web.servlet.handler.HandlerExceptionResolverComposite#resolveException

public ModelAndView resolveException(
	HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

	if (this.resolvers != null) {
		for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
			/**
				 * @see AbstractHandlerExceptionResolver#resolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
				 */
			ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
			if (mav != null) {
				return mav;
			}
		}
	}
	return null;
}

然后会调用各个HandlerExceptionResolver的doResolveException()进行异常处理。

org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver#resolveException

public ModelAndView resolveException(
	HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

	if (shouldApplyTo(request, handler)) {
		prepareResponse(ex, response);
		/**
			 * @see DefaultHandlerExceptionResolver#doResolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
			 * @see ResponseStatusExceptionResolver#doResolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
			 * @see SimpleMappingExceptionResolver#doResolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
			 * @see AbstractHandlerMethodExceptionResolver#doResolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
			 *
			 */
		// 让所有的ExceptionResolver按顺序挨个处理,有视图返回就救赎
		ModelAndView result = doResolveException(request, response, handler, ex);
		if (result != null) {
			// Print debug message when warn logger is not enabled.
			if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
				logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
			}
			// Explicitly configured warn logger in logException method.
			logException(ex, request);
		}
		return result;
	}
	else {
		return null;
	}
}

在这里插入图片描述

ExceptionHandlerExceptionResolver

ExceptionHandlerExceptionResolver从名字就可以看出是处理@ExceptionHandler注解的。

首先看下ExceptionHandlerExceptionResolver的afterPropertiesSet方法,主要解析@ControllerAdvice注解的类中的带有@ExceptionHandler的方法,建议异常与方法之间的映射关系,注意这里只解析了@ControllerAdvice注解的类中的带有@ExceptionHandler注解的方法,并没有解析Controller中带有ExceptionHandler注解的方法。

org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#afterPropertiesSet

public void afterPropertiesSet() {
	// Do this first, it may add ResponseBodyAdvice beans
	// 解析带有@ControllerAdvice注解的类
	initExceptionHandlerAdviceCache();

	if (this.argumentResolvers == null) {
		List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
		this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
	}
	if (this.returnValueHandlers == null) {
		List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
		this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
	}
}

private void initExceptionHandlerAdviceCache() {
	if (getApplicationContext() == null) {
		return;
	}

	// 从容器中查找所有带有ControllerAdvice注解的类
	List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
	for (ControllerAdviceBean adviceBean : adviceBeans) {
		Class<?> beanType = adviceBean.getBeanType();
		if (beanType == null) {
			throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
		}
		// 解析类中带有@ExceptionHandler的方法
		ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
		if (resolver.hasExceptionMappings()) {
			this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
		}
		if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
			this.responseBodyAdvice.add(adviceBean);
		}
	}

	if (logger.isDebugEnabled()) {
		int handlerSize = this.exceptionHandlerAdviceCache.size();
		int adviceSize = this.responseBodyAdvice.size();
		if (handlerSize == 0 && adviceSize == 0) {
			logger.debug("ControllerAdvice beans: none");
		}
		else {
			logger.debug("ControllerAdvice beans: " +
						 handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");
		}
	}
}

ExceptionHandlerExceptionResolver对异常的处理就是找到@ExceptionHandler注解的方法,并进行反射调用。

protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
													   HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {

	// 不管是Controller中的@ExceptionHandler,还是ControllerAdvice中的@ExceptionHandler
	// 最终异常的处理都是要找到带有@ExceptionHandler注解的方法进行调用
	ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
	if (exceptionHandlerMethod == null) {
		return null;
	}

	if (this.argumentResolvers != null) {
		exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
	}
	if (this.returnValueHandlers != null) {
		exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
	}

	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	ModelAndViewContainer mavContainer = new ModelAndViewContainer();

	try {
		if (logger.isDebugEnabled()) {
			logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
		}
		Throwable cause = exception.getCause();
		if (cause != null) {
			// Expose cause as provided argument as well
			exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
		}
		else {
			// Otherwise, just the given exception as-is
			exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
		}
	}
	catch (Throwable invocationEx) {
		// Any other than the original exception (or its cause) is unintended here,
		// probably an accident (e.g. failed assertion or the like).
		if (invocationEx != exception && invocationEx != exception.getCause() && logger.isWarnEnabled()) {
			logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
		}
		// Continue with default processing of the original exception...
		return null;
	}

	if (mavContainer.isRequestHandled()) {
		return new ModelAndView();
	}
	else {
		ModelMap model = mavContainer.getModel();
		HttpStatus status = mavContainer.getStatus();
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
		mav.setViewName(mavContainer.getViewName());
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
		return mav;
	}
}

@Nullable
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
	@Nullable HandlerMethod handlerMethod, Exception exception) {

	Class<?> handlerType = null;

	if (handlerMethod != null) {
		// Local exception handler methods on the controller class itself.
		// To be invoked through the proxy, even in case of an interface-based proxy.
		handlerType = handlerMethod.getBeanType();
		ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
		if (resolver == null) {
			// 解析controller中带有@ExceptionHandler注解的方法
			resolver = new ExceptionHandlerMethodResolver(handlerType);
			this.exceptionHandlerCache.put(handlerType, resolver);
		}
		// 根据异常找到@ExceptionHandler注解的方法
		Method method = resolver.resolveMethod(exception);
		if (method != null) {
			return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
		}
		// For advice applicability check below (involving base packages, assignable types
		// and annotation presence), use target class instead of interface-based proxy.
		if (Proxy.isProxyClass(handlerType)) {
			handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
		}
	}

	// 这里@ControllerAdvice全局异常的处理
	// exceptionHandlerAdviceCache属性在afterProperties()方法中初始化
	for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
		ControllerAdviceBean advice = entry.getKey();
		if (advice.isApplicableToBeanType(handlerType)) {
			ExceptionHandlerMethodResolver resolver = entry.getValue();
			// 根据异常找到@ExceptionHandler注解的方法
			Method method = resolver.resolveMethod(exception);
			if (method != null) {
				return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
			}
		}
	}

	return null;
}

ResponseStatusExceptionResolver

ResponseStatusExceptionResolver主要是要拿到异常类上面的@ResponseStatus注解,然后将response的状态和内容设置为@ResponseStatus注解中的内容。

org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#doResolveException

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

	try {
		if (ex instanceof ResponseStatusException) {
			return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
		}

		// 拿到异常上面的@ResponseStatus注解
		ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
		if (status != null) {
			// 设置错误码和错误内容
			return resolveResponseStatus(status, request, response, handler, ex);
		}

		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);
		}
	}
	return null;
}

DefaultHandlerExceptionResolver

DefaultHandlerExceptionResolver是用来处理spring mvc内部异常,如HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException等异常。

protected ModelAndView doResolveException(
	HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
	// 处理spring内部异常
	try {
		if (ex instanceof HttpRequestMethodNotSupportedException) {
			return handleHttpRequestMethodNotSupported(
				(HttpRequestMethodNotSupportedException) ex, request, response, handler);
		}
		else if (ex instanceof HttpMediaTypeNotSupportedException) {
			return handleHttpMediaTypeNotSupported(
				(HttpMediaTypeNotSupportedException) ex, request, response, handler);
		}
		else if (ex instanceof HttpMediaTypeNotAcceptableException) {
			return handleHttpMediaTypeNotAcceptable(
				(HttpMediaTypeNotAcceptableException) ex, request, response, handler);
		}
		else if (ex instanceof MissingPathVariableException) {
			return handleMissingPathVariable(
				(MissingPathVariableException) ex, request, response, handler);
		}
		else if (ex instanceof MissingServletRequestParameterException) {
			return handleMissingServletRequestParameter(
				(MissingServletRequestParameterException) ex, request, response, handler);
		}
		else if (ex instanceof ServletRequestBindingException) {
			return handleServletRequestBindingException(
				(ServletRequestBindingException) ex, request, response, handler);
		}
		else if (ex instanceof ConversionNotSupportedException) {
			return handleConversionNotSupported(
				(ConversionNotSupportedException) ex, request, response, handler);
		}
		else if (ex instanceof TypeMismatchException) {
			return handleTypeMismatch(
				(TypeMismatchException) ex, request, response, handler);
		}
		else if (ex instanceof HttpMessageNotReadableException) {
			return handleHttpMessageNotReadable(
				(HttpMessageNotReadableException) ex, request, response, handler);
		}
		else if (ex instanceof HttpMessageNotWritableException) {
			return handleHttpMessageNotWritable(
				(HttpMessageNotWritableException) ex, request, response, handler);
		}
		else if (ex instanceof MethodArgumentNotValidException) {
			return handleMethodArgumentNotValidException(
				(MethodArgumentNotValidException) ex, request, response, handler);
		}
		else if (ex instanceof MissingServletRequestPartException) {
			return handleMissingServletRequestPartException(
				(MissingServletRequestPartException) ex, request, response, handler);
		}
		else if (ex instanceof BindException) {
			return handleBindException((BindException) ex, request, response, handler);
		}
		else if (ex instanceof NoHandlerFoundException) {
			return handleNoHandlerFoundException(
				(NoHandlerFoundException) ex, request, response, handler);
		}
		else if (ex instanceof AsyncRequestTimeoutException) {
			return handleAsyncRequestTimeoutException(
				(AsyncRequestTimeoutException) ex, request, response, handler);
		}
	}
	catch (Exception handlerEx) {
		if (logger.isWarnEnabled()) {
			logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
		}
	}
	return null;
}

SimpleMappingExceptionResolver

我们在构造SimpleMappingExceptionResolver时就会指定异常与视图的映射关系,在处理异常时只需要在映射map中获取即可。

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

	// Expose ModelAndView for chosen error view.
	// 将异常与属性exceptionMappings、excludedExceptions进行匹配,拿到视图名称
	String viewName = determineViewName(ex, request);
	if (viewName != null) {
		// Apply HTTP status code for error views, if specified.
		// Only apply it if we're processing a top-level request.
		Integer statusCode = determineStatusCode(request, viewName);
		if (statusCode != null) {
			applyStatusCodeIfPossible(request, response, statusCode);
		}
		return getModelAndView(viewName, ex, request);
	}
	else {
		return null;
	}
}

protected String determineViewName(Exception ex, HttpServletRequest request) {
	String viewName = null;
	if (this.excludedExceptions != null) {
		// 与传入的异常的类型进行匹配
		for (Class<?> excludedEx : this.excludedExceptions) {
			if (excludedEx.equals(ex.getClass())) {
				return null;
			}
		}
	}
	// Check for specific exception mappings.
	if (this.exceptionMappings != null) {
		// 与传入的异常的名字进行匹配
		viewName = findMatchingViewName(this.exceptionMappings, ex);
	}
	// Return default error view else, if defined.
	if (viewName == null && this.defaultErrorView != null) {
		if (logger.isDebugEnabled()) {
			logger.debug("Resolving to default view '" + this.defaultErrorView + "'");
		}
		viewName = this.defaultErrorView;
	}
	return viewName;
}
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐