Spring中的拦截器有三个函数,我们先给他们起个名字,方便称呼。
- preHandle 前置拦截
- postHandle 后置拦截
- afterCompletion 完成拦截
正常流程
DispatcherServlet#doDispatch
请求处理流程可以参照DispatcherServlet的doDispatch方法。将其简化如下:
public void doDispatch() {
try {
try {
getHandler();//获取拦截器和处理器
applyPreHandle();//触发前置拦截
handle();//执行处理方法
applyPostHandle();//触发后置处理
} catch (Exception e) {}
//处理异常,同时触发完成拦截
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception e) {
//触发完成拦截
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
}
可以看到,主要有两个try块。内部的try是对 前置处理–Controller处理–后置处理 进行异常捕获,进行全局异常处理。外部的try是为了捕获全局异常处理方法中抛出的异常。完成拦截是由 HandlerExecutionChain 执行的,它在执行的时候进行了try()catch, 如下图,所以完成拦截的异常不会被抛出。
流程图
有三点需要注意:
- 注意执行顺序。前置拦截是正序,后置拦截和完成拦截是倒序。
- 这是在前置拦截全都返回true的情况下的流程图。如果某一个前置拦截返回false,流程图如下图所示。
- ExceptionResolver代表我们自己实现HandlerExceptionResolver的类,或者在@ControllerAdvice中生命的@ExceptionHandler
异常流程
异常发生在preHandle
如果异常发生在前置拦截器,那么就会跳出内部的try块,进入 processDispatchResult 函数,这个函数负责拦截全局异常,并执行拦截器的 afterCompletion 函数。所以此时的处理流程如下图。表示第二个拦截器的 preHandle 发生异常,注意第二个拦截器的 afterCompletion 不会执行,只有绿色的部分会执行。
异常发生在处理器
异常发生在postHandle
异常发生在全局异常处理器
上面说到,在 processDispatchResult 函数中会执行全局异常处理,如果这里出现异常,就会跳进外部的catch,触发拦截器的 afterCompletion 。此时的流程为:
异常发生在afterCompletion
拦截器的 afterCompletion 被try()catch包裹了,所以异常会被捕获,然后继续执行下一个拦截器的 afterCompletion 。如下图: