找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

907

积分

0

好友

114

主题
发表于 14 小时前 | 查看: 2| 回复: 0

Spring MVC拦截器执行流程图

在基于Spring MVC框架开发Web应用时,我们经常使用拦截器(Interceptor)来处理诸如接口鉴权、请求日志记录、全局参数校验等通用逻辑。你可能已经熟悉其基本用法,但你是否深入理解其背后的执行顺序?为何preHandle返回false就会中断整个请求流程?而postHandleafterCompletion的逆序执行又体现了怎样的设计哲学?本文将从DispatcherServlet的核心源码出发,为你揭开HandlerInterceptor的执行奥秘。

一、拦截器的“入口”:DispatcherServlet的doDispatch方法

所有拦截器的执行逻辑,都起始于DispatcherServletdoDispatch方法,这是Spring MVC处理HTTP请求的核心调度方法。以下是其简化后的关键源码:

// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    try {
        // 1. 获取HandlerExecutionChain(包含目标Controller和拦截器链)
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
        }

        // 2. 按序执行所有拦截器的preHandle方法
        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return; // 任一preHandle返回false,则中断后续所有流程
        }

        // 3. 实际执行Controller中的处理方法
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        // 4. 逆序执行所有拦截器的postHandle方法
        mappedHandler.applyPostHandle(processedRequest, response, mv);
    } catch (Exception ex) {
        // 异常处理逻辑
    } finally {
        // 5. 最终触发已成功执行preHandle的拦截器的afterCompletion方法
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(processedRequest, response, ex);
        }
    }
}

这里的核心对象是mappedHandler,它是HandlerExecutionChain类的一个实例。这个“执行链”对象封装了两部分内容:最终要执行的目标Controller(Handler),以及为此请求配置的所有拦截器(HandlerInterceptor)列表。整个请求的拦截器生命周期,都由此链上的三个核心方法驱动。

二、拦截器的“执行规则”:正序与逆序的巧妙设计

HandlerExecutionChain中定义了三个核心方法,分别对应拦截器的三个生命周期回调。

1. preHandle:正序执行,扮演“守门员”

preHandle是拦截器的前置处理方法,常用于进行访问权限校验、请求参数预检等。我们来看applyPreHandle方法的源码实现:

// HandlerExecutionChain.java
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 正序遍历拦截器列表
    for (int i = 0; i < this.interceptors.size(); i++) {
        HandlerInterceptor interceptor = this.interceptors.get(i);
        // 执行当前拦截器的preHandle
        if (!interceptor.preHandle(request, response, this.handler)) {
            // 如果返回false,则触发已执行拦截器的afterCompletion(逆序)
            triggerAfterCompletion(request, response, null);
            return false;
        }
        // 记录最后一个成功执行preHandle的拦截器索引
        this.interceptorIndex = i;
    }
    return true;
}

关键点解析:

  • 正序执行:拦截器按照配置文件的顺序依次执行(例如配置了A、B、C,则执行顺序为A→B→C)。
  • 中断机制:若任意拦截器的preHandle返回false,则立即终止流程,不会执行后续拦截器的preHandle,更不会执行目标Controller。同时,会逆序触发已成功执行了preHandle的那些拦截器的afterCompletion方法,用于资源清理。
2. postHandle:逆序执行,进行“后置增强”

postHandle在Controller方法执行之后、视图渲染之前被调用,可用于对ModelAndView进行修改,例如添加全局的模型数据。这在现代的SpringBoot Web应用实践中依然常见。其源码如下:

// HandlerExecutionChain.java
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
    // 逆序遍历拦截器列表
    for (int i = this.interceptors.size() - 1; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptors.get(i);
        interceptor.postHandle(request, response, this.handler, mv);
    }
}

设计思考:为何要逆序?
假设拦截器执行顺序是A→B→C(A最先,C最后)。postHandle的逆序(C→B→A)保证了最内层(最后执行的,如C)的拦截器先对处理结果进行增强,然后逐层向外传递。这种设计与“装饰器模式”或“栈”的结构类似,确保了处理逻辑的层次性和一致性,是后端架构中一种常见的责任链处理思想。

3. afterCompletion:逆序执行,完成“最终清理”

afterCompletion在整个请求结束时(视图渲染完毕后或发生异常时)被调用,适用于进行资源释放、性能监控记录等收尾工作。该方法无论请求处理成功或异常都会执行

// HandlerExecutionChain.java
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
    // 从最后一个成功执行preHandle的拦截器开始,逆序执行
    for (int i = this.interceptorIndex; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptors.get(i);
        try {
            interceptor.afterCompletion(request, response, this.handler, ex);
        } catch (Throwable ex2) {
            logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
        }
    }
}

特别注意:这里的循环变量i是从this.interceptorIndex开始的。该索引记录了在preHandle阶段最后一个成功执行的拦截器位置。因此,afterCompletion只会对那些preHandle执行成功的拦截器进行回调,并且执行顺序与postHandle相同,也是逆序的。

三、从UML时序图纵览全局流程

为了更清晰地把握整个交互过程,我们可以通过下面的UML时序图来可视化请求在拦截器链中的完整生命周期:

Spring MVC拦截器UML时序图

时序图关键节点解读:

  1. DispatcherServlet接收到客户端请求后,首先通过getHandler方法获取封装好的HandlerExecutionChain
  2. 调用链的applyPreHandle方法,正序执行各拦截器的preHandle。若中途返回false,则直接跳至triggerAfterCompletion进行逆序清理。
  3. Controller的业务方法被调用执行。
  4. Controller执行完毕后,调用链的applyPostHandle方法,逆序执行各拦截器的postHandle
  5. 最终,在finally块中,调用链的triggerAfterCompletion方法,逆序执行已成功通过preHandle的拦截器的afterCompletion

总结

Spring MVC拦截器的执行逻辑清晰体现了框架对请求生命周期的精细管理:preHandle是进入时的“安全检查”,postHandle是业务处理后的“增强修饰”,afterCompletion是离开时的“资源清理”。其“正序预处理,逆序后处理”的设计,既保证了逻辑的层层递进,也确保了资源释放的顺序安全。理解这一源码级细节,有助于我们在开发中更准确、更高效地运用拦截器这一强大组件,避免因执行顺序误解而导致的业务逻辑错误。




上一篇:金融黑灰产AIGC伪造风险加剧:技术对抗进入大小模型协同防御阶段
下一篇:miniSGLang轻量级LLM推理框架解析:架构、原理与性能实测
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-24 17:08 , Processed in 0.145698 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表