解读Spring Boot框架中不同位置抛出异常的处理流程
背景知识
对于Java Web开发而言,客户端发起的HTTP请求处理顺序为:Servlet容器 -> Filter -> Servlet -> Interceptor -> Controller,参考:Spring拦截器HandlerInterceptor与Filter方法执行顺序探究。
如下是一个客户端HTTP请求到达Serlvet容器后的完整执行流程:
-> org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun() -------
-> org.apache.coyote.AbstractProtocol$ConnectionHandler.process() |
-> org.apache.coyote.AbstractProcessorLight.process() |
-> org.apache.coyote.http11.Http11Processor.service() |
-> org.apache.catalina.connector.CoyoteAdapter.service() |
-> org.apache.catalina.core.StandardEngineValve.invoke() Servlet容器
-> org.apache.catalina.valves.ErrorReportValve.invoke() |
-> org.apache.catalina.core.StandardHostValve.invoke() |
-> org.apache.catalina.authenticator.AuthenticatorBase.invoke() |
-> org.apache.catalina.core.StandardContextValve.invoke() |
-> org.apache.catalina.core.StandardWrapperValve.invoke() |
-> org.apache.catalina.core.ApplicationFilterChain.doFilter() |
-> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter() -------
-> 执行Filter.doFilter():如果在这里抛出了异常,在StandardWrapperValve.invoke()方法中会被捕获,并将异常对象保存到request属性中,返回到StandardHostValve.invoke()方法后检查request属性中的异常对象是否为空,如果不为空就执行RequestDispatcher.formard()访问/error路径;如果在Filter中未抛出异常,继续执行Servlet -> Interceptor -> Controller
-> javax.servlet.http.HttpServlet.service()
-> org.springframework.web.servlet.FrameworkServlet.service()
-> org.springframework.web.servlet.FrameworkServlet.doGet()等
-> org.springframework.web.servlet.FrameworkServlet.processRequest()
-> org.springframework.web.servlet.DispatcherServlet.processRequest()
-> org.springframework.web.servlet.DispatcherServlet.doService()
-> org.springframework.web.servlet.DispatcherServlet.doDispatch()
-> org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle()
-> 执行HandlerInterceptor.preHandle()
-> org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle()
-> org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal()
-> org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod()
-> org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle()
-> org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest()
-> org.springframework.web.method.support.InvocableHandlerMethod.doInvoke()
-> 执行Controller方法
-> org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue()
-> org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue()
-> org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters()
-> org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyAdviceChain.beforeBodyWrite()
-> org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice.beforeBodyWrite()
<-
<-
<-
-> org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.getModelAndView()
<-
<-
:如果在HandlerInterceptor.preHandle()方法或者Controller方法中抛出异常,会在DispatcherServlet.doDispatch()方法中被捕获,进入到DispatcherServlet.processDispatchResult()方法,最后调用全局异常拦截器进行处理
-> org.springframework.web.servlet.DispatcherServlet.applyDefaultViewName()
-> 执行HandlerInterceptor.postHandle()
-> 执行HandlerInterceptor.afterCompletion()
<-
<-
<-
<-
<-
<-
<-
<-
<-
<-
<-
<-
<- 将响应数据写回客户端
在Filter中抛出异常
客户端发起的HTTP请求最先到达的组件就是Filter,如果在Filter.doFilter()方法中抛出了异常,在StandardWrapperValve.invoke()方法中会被捕获,并将异常对象保存到request属性中,返回到StandardHostValve.invoke()方法后检查request属性中的异常对象是否为空,如果不为空就执行RequestDispatcher.formard()访问/error路径。
// org.apache.catalina.core.StandardWrapperValve
private void exception(Request request, Response response,
Throwable exception) {
// 将在Filter.doFilter()方法中抛出的异常对象保存到request属性中,属性名为RequestDispatcher.ERROR_EXCEPTION,即:"javax.servlet.error.exception"
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, exception);
// 设置HTTP响应状态码
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
// 设置错误状态标志
response.setError();
}
// org.apache.catalina.core.StandardHostValve
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// 省略其他代码...
try {
// 省略其他代码...
// 从request属性中获取异常对象
Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
// Protect against NPEs if the context was destroyed during a
// long running request.
if (!context.getState().isAvailable()) {
return;
}
// Look for (and render if found) an application level error page
if (response.isErrorReportRequired()) {
if (t != null) {
// 如果异常不为空,
throwable(request, response, t);
} else {
status(request, response);
}
}
// 省略其他代码...
} finally {
// 省略其他代码...
}
}
protected void throwable(Request request, Response response,
Throwable throwable) {
// 省略其他代码...
status(request, response);
// 省略其他代码...
}
// 在custom()方法中调用RequestDispatcher.forward()访问/error路径
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);
// 执行RequestDispatcher.forward()方法访问错误页面(默认为/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;
}
}
正因为如此,在Filter中抛出的异常无法被全局异常拦截器处理,如果希望在全局异常拦截器中处理Filter中抛出的异常,需要自定义org.springframework.boot.autoconfigure.web.ErrorController实现。
// 自定义org.springframework.boot.autoconfigure.web.ErrorController实现,将在Filter中抛出的异常交给全局异常拦截器处理
@RequestMapping("/error")
public class DefaultErrorController implements ErrorController {
@RequestMapping
public void handleError(HttpServletRequest req) throws Throwable {
Object exceptAttr = req.getAttribute("javax.servlet.error.exception");
if (exceptAttr != null) {
// 将异常信息抛给全局异常拦截器处理
throw (Throwable) exceptAttr;
}
}
}
在HandlerInterceptor或Controller中抛出异常
如果在HandlerInterceptor.preHandle()方法或者Controller方法中抛出异常,会在DispatcherServlet.doDispatch()方法中被捕获,进入到DispatcherServlet.processDispatchResult()方法,最后调用全局异常拦截器进行处理。
// org.springframework.web.servlet.DispatcherServlet
// 省略其他代码...
/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, 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);
}
}
// 省略其他代码...
if (mappedHandler != null) {
// 不论HandlerInterceptor.preHandle()方法或Controller方法是否抛出异常,HandlerInterceptor.afterCompletion()方法总是会被调用
// 特别注意:如果HandlerInterceptor.preHandle()方法未抛出异常,而是明确返回了false,HandlerInterceptor.afterCompletion()方法不会被调用
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
// 在这里通过全局异常拦截器处理在Controller或HandlerInterceptor.preHandle()方法中抛出的异常
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
// 省略其他代码...
}
解读完毕。
作者:编程随笔
出处:http://www.cnblogs.com/nuccch/
声明:本文版权归作者和博客园共有,欢迎转载,但请在文章页面明显位置给出原文连接。

浙公网安备 33010602011771号