抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

SpringMVC-DispatcherServlet流程解析

前言

​ 在Spring MVC中处理HTTP请求时如果抛出异常会使用DispatcherServlet#processHandlerException()处理,这个方法内部使用Spring MVC默认的注册的HandlerExceptionResolver进行处理。

processHandlerException

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@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) {
//遍历异常处理器
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
//调用异常处理器的resolveException方法
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
//在request中设置异常信息
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
//如果没有设置viewName
if (!exMv.hasView()) {
//获取默认的viewName
String defaultViewName = getDefaultViewName(request);
//并设置默认的viewName
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;
}

Spring MVC默认注册了三个HandlerExceptionResolver:

  1. ExceptionHandlerExceptionResolver
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver

HandlerExceptionResolver

是异常处理器的接口

1
2
3
4
5
6
7
8
9
10
11
12
public interface HandlerExceptionResolver {

/**
* 尝试解决在处理程序执行期间抛出的给定异常,返回表示特定错误页面的ModelAndView(如果适用)。
* 返回的ModelAndView可能是empty(ModelAndView.isEmpty()),表示异常已成功解析,但不应呈现任何视图,例如通过设置状态代码。
*/
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

}

继承关系

AbstractHandlerExceptionResolver

HandlerExceptionResolver实现的抽象基类。支持设置映射处理程序setMappedHandlers()和setMappedHandlerClasses(),解析器应该应用于并实现Ordered接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {

private static final String HEADER_CACHE_CONTROL = "Cache-Control";


/**
* Logger available to subclasses.
*/
protected final Log logger = LogFactory.getLog(getClass());

private int order = Ordered.LOWEST_PRECEDENCE;

@Nullable
private Set<?> mappedHandlers;

@Nullable
private Class<?>[] mappedHandlerClasses;

@Nullable
private Log warnLogger;

private boolean preventResponseCaching = false;


public void setOrder(int order) {
this.order = order;
}

@Override
public int getOrder() {
return this.order;
}

/**
* 指定此异常解析程序应应用于的处理程序集合。异常映射和默认错误视图仅适用于指定的处理程序。
* 如果未设置处理程序或处理程序类,则异常映射和默认错误视图将应用于所有处理程序。
* 这意味着指定的默认错误视图将用作所有异常的回退; 在这种情况下,链中的任何其他HandlerExceptionResolvers都将被忽略。
*/
public void setMappedHandlers(Set<?> mappedHandlers) {
this.mappedHandlers = mappedHandlers;
}

/**
* 指定此异常解析程序应应用于的类集合。
* 异常映射和缺省错误视图仅适用于指定类型的处理程序; 指定的类型也可以是处理程序的接口或超类。
* 如果未设置处理程序或处理程序类,则异常映射和默认错误视图将应用于所有处理程序。
* 这意味着指定的默认错误视图将用作所有异常的回退; 在这种情况下,链中的任何其他HandlerExceptionResolvers都将被忽略。
*/
public void setMappedHandlerClasses(Class<?>... mappedHandlerClasses) {
this.mappedHandlerClasses = mappedHandlerClasses;
}


public void setWarnLogCategory(String loggerName) {
this.warnLogger = (StringUtils.hasLength(loggerName) ? LogFactory.getLog(loggerName) : null);
}

/**
* 指定是否阻止此异常解析程序解析的任何视图的HTTP响应缓存。
* 默认值为false。 将其切换为true,以便自动生成抑制响应缓存的HTTP响应标头。
*/
public void setPreventResponseCaching(boolean preventResponseCaching) {
this.preventResponseCaching = preventResponseCaching;
}


/**
* 检查是否应该应用此解析程序(即,所提供的处理程序是否与任何已配置的setMappedHandlers处理程序或setMappedHandlerClasses处理程序类匹配),
* 然后委托给doResolveException()模板方法。
*/
@Override
@Nullable
public ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

if (shouldApplyTo(request, handler)) {
prepareResponse(ex, response);
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;
}
}

/**
* 检查此解析程序是否应该应用于给定的处理程序。
* 默认实现检查配置的#setMappedHandlers处理程序和setMappedHandlerClasses处理程序类,如果有的话
*
*/
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
if (handler != null) {
if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
return true;
}
if (this.mappedHandlerClasses != null) {
for (Class<?> handlerClass : this.mappedHandlerClasses) {
if (handlerClass.isInstance(handler)) {
return true;
}
}
}
}
// Else only apply if there are no explicit handler mappings.
return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
}


protected void logException(Exception ex, HttpServletRequest request) {
if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
this.warnLogger.warn(buildLogMessage(ex, request));
}
}

protected String buildLogMessage(Exception ex, HttpServletRequest request) {
return "Resolved [" + ex + "]";
}

/**
* 为特殊情况准备响应。
* 如果setPreventResponseCaching preventResponseCaching属性已设置为”true“,则默认实现会阻止响应被缓存
*
*/
protected void prepareResponse(Exception ex, HttpServletResponse response) {
if (this.preventResponseCaching) {
preventCaching(response);
}
}

/**
* 通过设置相应的HTTP Cache-Control:no-store标头来阻止缓存响应。
*
*/
protected void preventCaching(HttpServletResponse response) {
response.addHeader(HEADER_CACHE_CONTROL, "no-store");
}


/**
* <p>
* 实际上解决了在处理程序执行期间抛出的给定异常,如果合适,返回表示特定错误页面的ModelAndView。
* 可以在子类中重写,以便应用特定的异常检查。
* 请注意,在检查此解析是否适用(“mappedHandlers”等)之后将调用此模板方法,因此实现可能只是继续其实际的异常处理。
*/
@Nullable
protected abstract ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

}

AbstractHandlerMethodExceptionResolver

HandlerExceptionResolver实现的抽象基类,支持处理类型为HandlerMethod的处理程序的异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {

/**
* 检查处理程序是否为HandlerMethod,然后委托shouldApplyTo(HttpServletRequest,Object)
* 的基类实现传递HandlerMethod的bean。 否则返回false
*/
@Override
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
if (handler == null) {
return super.shouldApplyTo(request, null);
} else if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
handler = handlerMethod.getBean();
return super.shouldApplyTo(request, handler);
} else {
return false;
}
}


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

return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
}

/**
* 实际上解决了在处理程序执行期间抛出的给定异常,返回表示特定错误页面的ModelAndView(如果适用)。
* 可以在子类中重写,以便应用特定的异常检查。
* 请注意,在检查此解析是否适用(“mappedHandlers”等)之后将调用此模板方法,因此实现可能只是继续其实际的异常处理。
*
*/
@Nullable
protected abstract ModelAndView doResolveHandlerMethodException(
HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception ex);

}

ExceptionHandlerExceptionResolver

是一个AbstractHandlerMethodExceptionResolver,它通过@ExceptionHandler方法解决异常。可以通过setCustomArgumentResolvers()和setCustomReturnValueHandlers()添加对自定义参数和返回值类型的支持。
或者,重新配置所有参数和返回值类型使用setArgumentResolvers()和setReturnValueHandlers(List)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/**
* InitializingBean 的接口实现类
* Spring初始化完成后会调用afterPropertiesSet接口
*/
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBodyAdvice beans
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;
}

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 value属性值异常或异常处理方法参数指定的异常
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");
}
}
}

/**
* Return an unmodifiable Map with the {@link ControllerAdvice @ControllerAdvice}
* beans discovered in the ApplicationContext. The returned map will be empty if
* the method is invoked before the bean has been initialized via
* {@link #afterPropertiesSet()}.
*/
public Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> getExceptionHandlerAdviceCache() {
return Collections.unmodifiableMap(this.exceptionHandlerAdviceCache);
}

/**
* Return the list of argument resolvers to use including built-in resolvers
* and custom resolvers provided via {@link #setCustomArgumentResolvers}.
*/
protected List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

// Annotation-based argument resolution
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());

// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());

// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}

return resolvers;
}

/**
* Return the list of return value handlers to use including built-in and
* custom handlers provided via {@link #setReturnValueHandlers}.
*/
protected List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();

// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new HttpEntityMethodProcessor(
getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));

// Annotation-based return value types
handlers.add(new ModelAttributeMethodProcessor(false));
handlers.add(new RequestResponseBodyMethodProcessor(
getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));

// Multi-purpose return value types
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());

// Custom return value types
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}

// Catch-all
handlers.add(new ModelAttributeMethodProcessor(true));

return handlers;
}


/**
* Find an {@code @ExceptionHandler} method and invoke it to handle the raised exception.
* 找到@ExceptionHandler方法并调用它来处理引发的异常
*/
@Override
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {

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;
}
}

/**
* Find an {@code @ExceptionHandler} method for the given exception. The default
* implementation searches methods in the class hierarchy of the controller first
* and if not found, it continues searching for additional {@code @ExceptionHandler}
* methods assuming some {@linkplain ControllerAdvice @ControllerAdvice}
* Spring-managed beans were detected.
* <p>
* 找到给定异常的@ExceptionHandler方法。
* 默认实现首先搜索控制器的类层次结构中的方法,如果没有找到,它将继续搜索其他@ExceptionHandler方法,
* 假设检测到一些@ControllerAdvice Spring管理的bean。
*
* @param handlerMethod the method where the exception was raised (may be {@code null})
* @param exception the raised exception
* @return a method to handle the exception, or {@code null} if none
*/
@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) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
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());
}
}

for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
}
}
}

return null;
}

ResponseStatusExceptionResolver

一个HandlerExceptionResolver,它使用@ResponseStatus注释将异常映射到HTTP状态代码。

默认情况下,在{@link org.springframework.web.servlet.DispatcherServlet DispatcherServlet}以及MVC Java配置和MVC命名空间中启用此异常解析程序。

从4.2开始,这个解析器也会以@ResponseStatus递归查找原因异常,从4.2.2开始,此解析器支持自定义组合注释中@ResponseStatus的属性覆盖。

从5.0开始,这个解析器也支持ResponseStatusException。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {

@Nullable
private MessageSource messageSource;


@Override
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}


@Override
@Nullable
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 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;
}

/**
* 处理{@link ResponseStatus @ResponseStatus}注释的模板方法。
* 默认实现使用注释中的状态代码和原因委托给applyStatusAndReason()。
*
*/
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);
}

/**
* 处理{@link ResponseStatusException}的模板方法。
* 默认实现使用状态代码和异常原因委托给applyStatusAndReason()。
*/
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);
}

/**
* 将已解决的状态代码和原因应用于响应。
* 如果有原因,默认实现使用HttpServletResponse#sendError(int)
* 或HttpServletResponse#sendError(int,String)发送响应错误,
* 然后返回空的ModelAndView。
*/
protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response)
throws IOException {

if (!StringUtils.hasLength(reason)) {
response.sendError(statusCode);
} else {
String resolvedReason = (this.messageSource != null ?
this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
reason);
response.sendError(statusCode, resolvedReason);
}
return new ModelAndView();
}

}

DefaultHandlerExceptionResolver

HandlerExceptionResolver接口的默认实现,解析标准的Spring MVC异常并将它们转换为相应的HTTP状态代码。
默认情况下,在常见的Spring DispatcherServlet中启用此异常解析程序

支持的异常
异常 HTTP状态码
HttpRequestMethodNotSupportedException 405 (SC_METHOD_NOT_ALLOWED)
HttpMediaTypeNotSupportedException 415 (SC_UNSUPPORTED_MEDIA_TYPE)
HttpMediaTypeNotAcceptableException 406 (SC_NOT_ACCEPTABLE)
MissingPathVariableException 500 (SC_INTERNAL_SERVER_ERROR)
MissingServletRequestParameterException 400 (SC_BAD_REQUEST)
ServletRequestBindingException 400 (SC_BAD_REQUEST)
ConversionNotSupportedException 500 (SC_INTERNAL_SERVER_ERROR)
TypeMismatchException 400 (SC_BAD_REQUEST)
HttpMessageNotReadableException 400 (SC_BAD_REQUEST)
HttpMessageNotWritableException 500 (SC_INTERNAL_SERVER_ERROR)
MethodArgumentNotValidException 400 (SC_BAD_REQUEST)
MissingServletRequestPartException 400 (SC_BAD_REQUEST)
BindException 400 (SC_BAD_REQUEST)
NoHandlerFoundException 404 (SC_NOT_FOUND)
AsyncRequestTimeoutException 503 (SC_SERVICE_UNAVAILABLE)

评论