【Spring】HandlerExceptionResolver异常处理器工作原理分析

编程

HandlerExceptionResolver Spring 全局异常处理

常用异常处理器

  • DefaultErrorAttributes: 用于BasicErrorController用于错误信息视图展示
  • ExceptionHandlerExceptionResolver: 请求和响应参数处理异常
  • ResponseStatusExceptionResolver: 响应状态码异常处理: @ResponseStatus 指定特定响应码异常处理,例如设置自定义提示信息
  • DefaultHandlerExceptionResolver: 默认异常处理器,常见例如:HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException

HandlerExceptionResolver工作原理

1. 自定义异常处理器

源码:org.springframework.web.servlet.DispatcherServlet.initHandlerExceptionResolvers

private void initHandlerExceptionResolvers(ApplicationContext context) {

this.handlerExceptionResolvers = null;

if (this.detectAllHandlerExceptionResolvers) {

// 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) {

this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);

if (logger.isTraceEnabled()) {

logger.trace("No HandlerExceptionResolvers declared in servlet "" + getServletName() +

"": using default strategies from DispatcherServlet.properties");

}

}

}

结果:默认 2个异常处理器

DefaultErrorAttributes

HandlerExceptionResolverComposite:

ExceptionHandlerExceptionResolver

ResponseStatusExceptionResolver

DefaultHandlerExceptionResolver

2. 异常处理逻辑

源码: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) {

// 异常处理器依次处理,返回第一个能处理异常的异常视图

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

}

if (logger.isDebugEnabled()) {

logger.debug("Using resolved error view: " + exMv);

}

WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());

return exMv;

}

throw ex;

}

3. 异常案例:HttpRequestMethodNotSupportedException

异常处理器:DefaultHandlerExceptionResolver

源码:org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.doResolveException

@Override

@Nullable

protected ModelAndView doResolveException(

HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

try {

// 目标方法Http请求方法不支持

if (ex instanceof HttpRequestMethodNotSupportedException) {

return handleHttpRequestMethodNotSupported(

(HttpRequestMethodNotSupportedException) ex, request, response, handler);

}

//Http 媒体类型不支持

else if (ex instanceof HttpMediaTypeNotSupportedException) {

return handleHttpMediaTypeNotSupported(

(HttpMediaTypeNotSupportedException) ex, request, response, handler);

}

// 不接收指定类型媒体类型

else if (ex instanceof HttpMediaTypeNotAcceptableException) {

return handleHttpMediaTypeNotAcceptable(

(HttpMediaTypeNotAcceptableException) ex, request, response, handler);

}

// http请求路径变量参数缺失:例如@PathVariable指定的变量名,http请求路径为指定对应占位符

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

}

// 转换错误:例如将String转成Integer

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;

}

4. HttpRequestMethodNotSupportedException详细处理

源码:

protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,

HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {

String[] supportedMethods = ex.getSupportedMethods();

if (supportedMethods != null) {

response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));

}

// 原生Sevlet API响应 405 请求, Tomcat按照ErrorPage路径处理

response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());

return new ModelAndView();

}

5. Tomcat 错误页处理

Tomcat默认配置:org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.configureContext

protected void configureContext(Context context,

ServletContextInitializer[] initializers) {

TomcatStarter starter = new TomcatStarter(initializers);

if (context instanceof TomcatEmbeddedContext) {

TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;

embeddedContext.setStarter(starter);

embeddedContext.setFailCtxIfServletStartFails(true);

}

context.addServletContainerInitializer(starter, NO_CLASSES);

for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {

context.addLifecycleListener(lifecycleListener);

}

for (Valve valve : this.contextValves) {

context.getPipeline().addValve(valve);

}

// Tomcat错误页处理:ErroPage路径配置在:ErrorProperties path(@Value("${error.path:/error}")),默认Error

for (ErrorPage errorPage : getErrorPages()) {

new TomcatErrorPage(errorPage).addToContext(context);

}

for (MimeMappings.Mapping mapping : getMimeMappings()) {

context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());

}

configureSession(context);

new DisableReferenceClearingContextCustomizer().customize(context);

for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {

customizer.customize(context);

}

}

6. Tomcat ErrorPage 转发处理

源码:org.apache.catalina.core.StandardHostValve.custom

 private boolean custom(Request request, Response response,

ErrorPage errorPage) {

if (container.getLogger().isDebugEnabled()) {

container.getLogger().debug("Processing " + errorPage);

}

try {

// Forward control to the specified location

ServletContext servletContext = request.getContext().getServletContext();

RequestDispatcher rd = servletContext.getRequestDispatcher(errorPage.getLocation());

if (rd == null) {

container.getLogger().error(sm.getString("standardHostValue.customStatusFailed", errorPage.getLocation()));

return false;

}

if (response.isCommitted()) {

// Response is committed - including the error page is the

// best we can do

rd.include(request.getRequest(), response.getResponse());

} else {

// Reset the response (keeping the real error code and message)

response.resetBuffer(true);

response.setContentLength(-1);

// 请求转发:/error

rd.forward(request.getRequest(), response.getResponse());

// If we forward, the response is suspended again

response.setSuspended(false);

}

// Indicate that we have successfully processed this custom page

return true;

} catch (Throwable t) {

ExceptionUtils.handleThrowable(t);

// Report our failure to process this custom page

container.getLogger().error("Exception Processing " + errorPage, t);

return false;

}

}

7. Tomcat ErrorPage(/error) 请求转发(BasicErrorController),完成异常信息响应

org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error

/**

* 完成异常信息视图处理

/

@RequestMapping

public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {

Map<String, Object> body = getErrorAttributes(request,isIncludeStackTrace(request, MediaType.ALL));

HttpStatus status = getStatus(request);

return new ResponseEntity<>(body, status);

}

// 从异常处理器errorAttributes 取出相关异常信息

protected Map<String, Object> getErrorAttributes(HttpServletRequest request,

boolean includeStackTrace) {

WebRequest webRequest = new ServletWebRequest(request);

return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);

}

//获取异常状态码

protected HttpStatus getStatus(HttpServletRequest request) {

Integer statusCode = (Integer) request

.getAttribute("javax.servlet.error.status_code");

if (statusCode == null) {

return HttpStatus.INTERNAL_SERVER_ERROR;

}

try {

return HttpStatus.valueOf(statusCode);

}

catch (Exception ex) {

return HttpStatus.INTERNAL_SERVER_ERROR;

}

}

测试异常提示:

{

"timestamp": "2020-06-22T02:29:27.424+0000",

"status": 405,

"error": "Method Not Allowed",

"message": "Request method "GET" not supported",

"path": "/dev/file/down"

}

以上是 【Spring】HandlerExceptionResolver异常处理器工作原理分析 的全部内容, 来源链接: utcz.com/z/517715.html

回到顶部