【springmvc】九大组件之HandlerExceptionResolver
在Spring MVC中,如果对异常不做任何处理,Spring MVC会将异常直接抛给容器。例如下面的代码抛出了异常:@GetMapping("e1")public String exception() {int i = 1 / 0;return "exception";}浏览器页面上会显示500错误。对异常的处理@ExceptionHandler处理当前Controller的异常@Excepti
在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()方法。
下面的这两种异常场景不适用:
- 请求没有到达DispatcherServlet的核心流程,如在filter中抛出异常。
- 请求进入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;
}
更多推荐
所有评论(0)