近期在面试的时候问到了 spring mvc 的 HandlerInterceptor,用过但是没深入,记录一下。

以下代码为 spring boot 2.7.15 中自带的 spring 5.3.29

如下为 DispatcherServlet 中的 doDispatch()

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

HttpServletRequest processedRequest = request;

HandlerExecutionChain mappedHandler = null;

boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {

ModelAndView mv = null;

Exception dispatchException = null;

try {

processedRequest = checkMultipart(request);

multipartRequestParsed = (processedRequest != request);

// 确定当前请求的处理器 HandlerExecutionChain

mappedHandler = getHandler(processedRequest);

if (mappedHandler == null) {

noHandlerFound(processedRequest, response);

return;

}

// 确定当前请求的处理适配器 HandlerAdapter,对应实现类为 RequestMappingHandlerAdapter

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

String method = request.getMethod();

boolean isGet = HttpMethod.GET.matches(method);

if (isGet || HttpMethod.HEAD.matches(method)) {

long lastModified = ha.getLastModified(request, mappedHandler.getHandler());

if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {

return;

}

}

// 针对定义的 HandlerInterceptor 重写 preHandle() 处理对应的逻辑

// 执行时机:HandlerExecutionChain 对象中在进入 handler 之前按序处理,如果任何一个 preHandle() 返回 false,则终止处理

if (!mappedHandler.applyPreHandle(processedRequest, response)) {

return;

}

// 实际调用 handler

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {

return;

}

applyDefaultViewName(processedRequest, mv);

// 针对定义的 HandlerInterceptor 重写 postHandle() 处理对应的逻辑,如果有多个,逆序处理

// 执行时机:HandlerExecutionChain 对象中在进入 handler 之后,渲染视图之前进行处理

mappedHandler.applyPostHandle(processedRequest, response, mv);

}

catch (Exception ex) {

dispatchException = ex;

}

catch (Throwable err) {

dispatchException = new NestedServletException("Handler dispatch failed", err);

}

// 针对定义的 HandlerInterceptor 重写 afterCompletion() 处理对应的逻辑,如果有多个,将执行完 preHandle() 后最后一个返回值为 true 的下标进行逆序处理

// 执行时机:HandlerExecutionChain 对象中在进入 handler 之后,渲染视图接触后进行处理

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

}

catch (Exception ex) {

triggerAfterCompletion(processedRequest, response, mappedHandler, ex);

}

catch (Throwable err) {

triggerAfterCompletion(processedRequest, response, mappedHandler,

new NestedServletException("Handler processing failed", err));

}

finally {

if (asyncManager.isConcurrentHandlingStarted()) {

// Instead of postHandle and afterCompletion

if (mappedHandler != null) {

mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);

}

}

else {

// Clean up any resources used by a multipart request.

if (multipartRequestParsed) {

cleanupMultipart(processedRequest);

}

}

}

}

其中通过 HandlerExecutionChain、HttpServletRequest、HttpServletResponse、HandlerAdapter、HandlerExecutionChain 来进行请求处理。

处理请求的是 HandlerAdapter 的实现类 RequestMappingHandlerAdapter。这是主要的请求流程。

HandlerExecutionChain 负责处理 HandlerInterceptor 的处理过程,作为辅助的功能。没有这个请求照样进行,但是没有 HandlerAdapter 就不行了。

HandlerInterceptor 是一个接口,其中有三个方法

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.lang.Nullable;

import org.springframework.web.method.HandlerMethod;

public interface HandlerInterceptor {

default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

throws Exception {

return true;

}

default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,

@Nullable ModelAndView modelAndView) throws Exception {

}

default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,

@Nullable Exception ex) throws Exception {

}

}

从 DispatcherServlet#doDispatch() 的代码逻辑可知,HandlerInterceptor 的执行顺序为

preHandle()

postHandle()

afterCompletion()

preHandle()

针对定义的 HandlerInterceptor 重写 preHandle() 处理对应的逻辑。

执行时机:HandlerExecutionChain 对象中在进入 handler 之前按序处理,如果任何一个 preHandle() 返回 false,则终止处理。

postHandle()

针对定义的 HandlerInterceptor 重写 postHandle() 处理对应的逻辑,如果有多个,逆序处理。

执行时机:HandlerExecutionChain 对象中在进入 handler 之后,渲染视图之前进行处理。

即 postHandle() 要执行,则 preHandle() 的返回值必须为 true。

afterCompletion()

针对定义的 HandlerInterceptor 重写 afterCompletion() 处理对应的逻辑,如果有多个,将执行完 preHandle() 后最后一个返回值为 true 的下标进行逆序处理。

执行时机:HandlerExecutionChain 对象中在进入 handler 之后,渲染视图接触后进行处理。

HandlerInterceptor 的三个方法具体执行逻辑在 HandlerExecutionChain 中。

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {

for (int i = 0; i < this.interceptorList.size(); i++) {

HandlerInterceptor interceptor = this.interceptorList.get(i);

if (!interceptor.preHandle(request, response, this.handler)) {

triggerAfterCompletion(request, response, null);

return false;

}

this.interceptorIndex = i;

}

return true;

}

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)

throws Exception {

for (int i = this.interceptorList.size() - 1; i >= 0; i--) {

HandlerInterceptor interceptor = this.interceptorList.get(i);

interceptor.postHandle(request, response, this.handler, mv);

}

}

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {

for (int i = this.interceptorIndex; i >= 0; i--) {

HandlerInterceptor interceptor = this.interceptorList.get(i);

try {

interceptor.afterCompletion(request, response, this.handler, ex);

}

catch (Throwable ex2) {

logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);

}

}

}

通过 postHandle() 和 afterCompletion() 的处理顺序让我想到了责任链模式。

看到这个想到了 servlet 的 Filter,拦截器能做的 Filter 也能做,并且不局限于 spring mvc,拦截器是 spring mvc 专有的功能,但是 servlet 是一个规范,只要一个服务对外暴露服务,使用了 servlet 容器处理这些服务(没有使用响应式处理)就可以。

Filter 的优先级比拦截器要高。

package javax.servlet;

import java.io.IOException;

public interface Filter {

default void init(FilterConfig filterConfig) throws ServletException {

}

void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

throws IOException, ServletException;

default void destroy() {

}

}

使用场景

权限校验

如果用户未登录,则在 preHandle() 方法处理后返回 false,进行登录处理。

性能监控

统计请求的耗时,在 preHandle() 和 afterCompletion() 中添加统计相关的处理逻辑。

日志记录

在 preHandle() 记录请求方式、请求参数等。

其中监控和日志可以通过 aop 来实现。

之前自己写的文章

https://blog.csdn.net/zlpzlpzyd/article/details/133499304

参考链接

https://www.zhihu.com/question/39510340/answer/3054411211

https://blog.csdn.net/cristianoxm/article/details/123215237

https://www.cnblogs.com/seanstudy/p/15848259.html

相关阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: