[Java] 纯文本查看 复制代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//...
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//1.获取执行链
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//2.获取处理器适配器
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
//...
//3.执行前置拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//4.执行业务handler
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
//5.执行后置拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
//6.处理页面响应,并执行最终拦截器
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
}finally {
//...
}
}
其中,第一步中"获取执行链",执行链内容可以截图查看:
可以看到我们自定义的两个拦截器按顺序保存。
在doDispatch方法中,我们注释的第3、5、6步骤是对拦截器的执行处理,现在分别来查看第3、5、6步骤的源码。
第三步骤源码如下:
[Java] 纯文本查看 复制代码
//3.执行前置拦截器中的详细代码
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
//获得本次请求对应的所有拦截器
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//按照拦截器顺序依次执行每个拦截器的preHandle方法.
//并且,interceptorIndex值会一次 + 1 (该值是给后面的最终拦截器使用的)
for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
HandlerInterceptor interceptor = interceptors;
//只要每个拦截器不返回false,则继续执行,否则执行最终拦截器
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
}
//最终返回true
return true;
}
我们可以看到拦截器的preHandler方法是按拦截器顺序执行的。紧接着贴出第5步的后置拦截器源码:
[Java] 纯文本查看 复制代码
//5.执行后置拦截器
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
//获得本次请求对应的所有拦截器
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//按倒叙执行每个拦截器的postHandle方法——所以我们看到先执行的拦截器2的postHandle,再执行拦截器1的postHandle
for(int i = interceptors.length - 1; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors;
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
会发现,后置处理是按照拦截器顺序倒叙处理的。最后,我们贴出执行最终拦截器方法的代码:
[Java] 纯文本查看 复制代码
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
//...
if (mv != null && !mv.wasCleared()) {
//处理响应
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");
}
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
//6、执行拦截器的最终方法们
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}
其中,有一个render()方法,该方法会直接处理完response。再后则是触发triggerAfterCompletion方法:
[Java] 纯文本查看 复制代码
//6、执行拦截器的最终
方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//倒叙执行每个拦截器(interceptorIndex为前置拦截器动态计算)的afterCompletion方法
for(int i = this.interceptorIndex; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors;
try {
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable var8) {
logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
}
}
}
}
由此可以看到,拦截器的最终方法的执行也是按照倒叙来执行的,而且是在响应之后。
7、总结
我们可以从源码中看到,拦截器类似于对我们业务方法的环绕通知效果,并且是通过循环集合来控制每个拦截器方法的执行顺序。
源码如下:
mvc_interceptor.zip
(13.49 KB, 下载次数: 383)