Spring MVC的HandlerExecutionChain:从快递分拣到灭霸响指的奇幻漂流

103 阅读5分钟

《Spring MVC的HandlerExecutionChain:从快递分拣到灭霸响指的奇幻漂流》


引言:当Spring MVC遇上快递小哥

想象一下,你网购了一箱肥宅快乐水,快递小哥却在中转站被保安拦下检查、被分拣员贴标签、被客服小姐姐温柔提醒——这就是HandlerExecutionChain的日常。它像一位忙碌的调度员,决定你的请求(快递)要经过哪些拦截器(中转站),最终由哪个Controller(你家)处理。今天,我们就来揭开这位"调度员"的神秘面纱!


一、介绍:HandlerExecutionChain是谁?

定义
HandlerExecutionChain是Spring MVC的“任务执行链”,封装了目标处理器(Handler)和一堆拦截器(Interceptors)。它像一条流水线,负责在请求处理前后“搞事情”。

组成

  • Handler:真正的打工人,比如@Controller中的方法。
  • Interceptors:一群“事多”的监工,比如日志记录、权限检查。

生命周期

  1. 快递分拣:DispatcherServlet根据URL找到合适的Handler,打包成Chain。
  2. 安检流程:拦截器们依次检查请求(preHandle)。
  3. 送货上门:Handler处理请求。
  4. 售后服务:拦截器们处理响应(postHandleafterCompletion)。

二、用法:如何“调教”HandlerExecutionChain?

1. 配置拦截器:给监工们发工牌

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor()).order(1); // 日志监工
        registry.addInterceptor(new AuthInterceptor()).order(2); // 保安监工
    }
}

2. 自定义拦截器:监工的自我修养

public class LogInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("监工日记:有人来买快乐水了!");
        return true; // 放行!false则直接退货
    }
}

三、案例:从肥宅快乐水到灭霸响指

场景:用户访问/buy/cola购买快乐水,需记录日志并检查钱包。

代码

// 1. 日志拦截器
public class LogInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("用户" + request.getRemoteUser() + "试图购买快乐水!");
        return true;
    }
}

// 2. 钱包检查拦截器
public class WalletInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (user.getBalance() < 5) {
            response.sendError(400, "余额不足,快去搬砖!");
            return false; // 拦截请求!
        }
        return true;
    }
}

// 3. Controller:处理购买请求
@RestController
public class ColaController {
    @GetMapping("/buy/cola")
    public String buyCola() {
        return "灭霸:你成功打了个响指,快乐水已送达!";
    }
}

四、原理:DispatcherServlet的“秘密会议”

  1. 找HandlerDispatcherServlet调用HandlerMapping,像查快递单号一样找到对应的Handler。
  2. 打包Chain:将Handler和拦截器们打包成HandlerExecutionChain
  3. 拦截器流水线
    • preHandle:拦截器们按顺序检查请求(像安检层层关卡)。
    • postHandle:Handler处理完后,拦截器们倒序加工响应(像给快递贴广告)。
    • afterCompletion:请求完成后收尾(无论成功与否,像清理快递站点)。

源码片段(简化版):

// DispatcherServlet.java
HandlerExecutionChain chain = getHandler(request);
if (chain.applyPreHandle(request, response)) {
    chain.handle(/*...*/); // 执行Handler
    chain.applyPostHandle(request, response, mv);
}
chain.triggerAfterCompletion(/*...*/);

五、对比:HandlerExecutionChain vs Filter

对比项HandlerExecutionChainFilter
管辖范围Spring MVC内部流程Servlet容器级别
拦截目标针对Handler(如Controller方法)所有请求(包括静态资源)
执行顺序在Filter之后执行在Interceptor之前执行
依赖关系依赖Spring容器不依赖Spring,纯Servlet

通俗版
Filter是小区门卫,Interceptor是楼栋保安,HandlerExecutionChain是负责协调保安和住户的物业经理。


六、避坑指南:别让拦截器变“拦精灵”

  1. 顺序问题
    拦截器默认按添加顺序执行,用order()方法明确排序,否则可能变成“先贴快递单再检查包裹”。

  2. preHandle返回值
    忘记return true?恭喜你,请求永远卡在第一个拦截器!

  3. 异步请求
    在异步处理中,afterCompletion可能提前触发,改用AsyncHandlerInterceptor

  4. 路径匹配
    拦截器配置路径时,/**表示所有路径,但别手滑写成/*(只匹配一级目录)。


七、最佳实践:做个优雅的“调度员”

  1. 职责单一
    一个拦截器只做一件事(比如日志记录和权限检查分开)。

  2. 避免阻塞
    别在拦截器里做耗时操作(比如查数据库),否则快递堆积成山。

  3. 合理排序
    权限检查拦截器应放在日志拦截器之后,避免记录无效请求。

  4. 善用排除
    excludePathPatterns("/static/**")放过静态资源,别让保安追着CSS文件跑。


八、面试考点:如何让面试官眼前一亮?

Q1:HandlerExecutionChain的作用是什么?
A:它像快递分拣系统,封装了处理请求的Handler和拦截器,管理请求的前后处理流程。

Q2:拦截器的执行顺序是怎样的?
A:preHandle按添加顺序执行,postHandleafterCompletion倒序执行,像“先安检再验票,回来时先发水再送客”。

Q3:拦截器和Filter有什么区别?
A:Filter是Servlet规范,拦截器是Spring的机制。Filter更底层,拦截器更贴合Spring MVC。

Q4:preHandle返回false会发生什么?
A:请求被拦截,后续拦截器和Handler都不执行,直接跳转到afterCompletion


九、总结:HandlerExecutionChain的终极奥义

HandlerExecutionChain就像灭霸的手套,Handler是宝石,Interceptor是灭霸的部下。只有正确组合它们,才能让请求处理如响指般优雅!记住:别让拦截器变成“拦精灵”,合理调度才是王道!

终极口诀

一链在手,Spring我有;
拦截有序,bug没有;
preHandle要放行,postHandle倒序走;
面试八股全不怕,Offer拿到手发抖!

(注:灭霸手套的宝石掉了概不负责,请妥善保管你的拦截器。)