设计模式:责任链模式(springmvc应用)

目录

一、核心角色对应(责任链模式)

[二、场景 1:HandlerInterceptor 拦截器链(核心)](#二、场景 1:HandlerInterceptor 拦截器链(核心))

[1. 核心流程(请求处理全生命周期)](#1. 核心流程(请求处理全生命周期))

[2. 代码示例(自定义拦截器链)](#2. 代码示例(自定义拦截器链))

[步骤 1:定义抽象处理者(复用 Spring 原生接口)](#步骤 1:定义抽象处理者(复用 Spring 原生接口))

[步骤 2:实现具体处理者(自定义拦截器)](#步骤 2:实现具体处理者(自定义拦截器))

[步骤 3:配置责任链(指定拦截器顺序和拦截路径)](#步骤 3:配置责任链(指定拦截器顺序和拦截路径))

[步骤 4:核心处理器(Controller,责任链终点)](#步骤 4:核心处理器(Controller,责任链终点))

[3. 执行效果(分场景)](#3. 执行效果(分场景))

[场景 1:请求已登录(token 有效)](#场景 1:请求已登录(token 有效))

[场景 2:请求未登录(token 无效)](#场景 2:请求未登录(token 无效))

[4. 责任链管理器:HandlerExecutionChain 核心逻辑](#4. 责任链管理器:HandlerExecutionChain 核心逻辑)

[三、场景 2:Servlet Filter 过滤器链(底层补充)](#三、场景 2:Servlet Filter 过滤器链(底层补充))

[1. 代码示例(自定义 Filter)](#1. 代码示例(自定义 Filter))

[2. 与 HandlerInterceptor 的核心区别](#2. 与 HandlerInterceptor 的核心区别)

[四、Spring MVC 责任链的核心特点](#四、Spring MVC 责任链的核心特点)

[1. 灵活性](#1. 灵活性)

[2. 中断性](#2. 中断性)

[3. 解耦性](#3. 解耦性)

五、典型应用场景

六、总结


Spring MVC 的责任链模式主要体现在两大核心场景:拦截器链(HandlerInterceptor)Servlet 过滤器链(Filter) ,其中 HandlerInterceptor 是 Spring MVC 原生封装的责任链,Filter 是基于 Servlet 规范的责任链,二者共同构成了请求处理的 "双层责任链"。

核心目标:将请求处理的通用逻辑(如登录校验、日志、跨域)与核心业务逻辑(Controller)解耦,支持按顺序执行、灵活扩展,且可中断请求流程

一、核心角色对应(责任链模式)

责任链模式角色 Spring MVC 对应实现 核心职责
抽象处理者(Handler) HandlerInterceptor 接口 / Filter 接口 定义处理方法(如 preHandle/doFilter),约定处理规则
具体处理者 自定义 HandlerInterceptor / Filter 实现抽象接口,处理特定逻辑(如登录校验、日志记录)
责任链管理器 HandlerExecutionChain(拦截器)/ FilterChain(过滤器) 管理处理者顺序,触发执行,控制请求传递
请求对象 HttpServletRequest/HttpServletResponse 被传递和处理的核心对象
最终处理者 HandlerAdapter + Controller(拦截器链终点)/ Servlet(过滤器链终点) 责任链传递的最终目标,处理核心业务逻辑

二、场景 1:HandlerInterceptor 拦截器链(核心)

1. 核心流程(请求处理全生命周期)

Spring MVC 拦截器链是不纯责任链(可中断、可部分处理),执行流程如下:

plaintext

复制代码
DispatcherServlet → 
  1. 构建 HandlerExecutionChain(包含拦截器列表 + Controller)→ 
  2. 按序执行拦截器 preHandle():
     - 若任意 preHandle 返回 false → 中断链,反向执行已通过拦截器的 afterCompletion();
     - 若全部返回 true → 传递到 Controller 执行核心业务 → 
  3. 倒序执行拦截器 postHandle()(仅 preHandle 全通过)→ 
  4. 视图渲染完成后,倒序执行拦截器 afterCompletion()(无论是否中断)

2. 代码示例(自定义拦截器链)

步骤 1:定义抽象处理者(复用 Spring 原生接口)

java

运行

复制代码
// 无需自定义抽象类,直接实现 Spring 提供的 HandlerInterceptor
public interface HandlerInterceptor {
    // 前置处理:请求到达 Controller 前执行,返回 false 中断链
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    // 后置处理:Controller 执行后、视图渲染前执行(仅 preHandle 全通过)
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}

    // 完成后处理:视图渲染后执行(无论是否中断,用于资源清理)
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
步骤 2:实现具体处理者(自定义拦截器)

java

运行

复制代码
// 1. 登录校验拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        if (token == null || !"valid_token".equals(token)) {
            response.setStatus(401);
            System.out.println("LoginInterceptor:未登录,中断请求");
            return false; // 中断责任链
        }
        System.out.println("LoginInterceptor:登录校验通过");
        return true; // 传递给下一个拦截器
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("LoginInterceptor:后置处理(Controller 执行后)");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("LoginInterceptor:完成后处理(资源清理)");
    }
}

// 2. 日志拦截器
@Component
public class LogInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("LogInterceptor:记录请求日志 - " + request.getRequestURI());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("LogInterceptor:记录响应日志 - " + response.getStatus());
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("LogInterceptor:归档日志");
    }
}
步骤 3:配置责任链(指定拦截器顺序和拦截路径)

java

运行

复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Autowired
    private LogInterceptor logInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器,指定执行顺序(order 越小,preHandle 越先执行)
        registry.addInterceptor(logInterceptor)
                .addPathPatterns("/**") // 拦截所有路径
                .order(1); // 日志拦截器先执行
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/api/**") // 仅拦截 /api 前缀路径
                .order(2); // 登录拦截器后执行
    }
}
步骤 4:核心处理器(Controller,责任链终点)

java

运行

复制代码
@RestController
@RequestMapping("/api")
public class UserController {
    @GetMapping("/user")
    public String getUser() {
        System.out.println("Controller:处理 /api/user 请求");
        return "success";
    }
}

3. 执行效果(分场景)

场景 1:请求已登录(token 有效)

plaintext

复制代码
LogInterceptor:记录请求日志 - /api/user
LoginInterceptor:登录校验通过
Controller:处理 /api/user 请求
LoginInterceptor:后置处理(Controller 执行后)
LogInterceptor:记录响应日志 - 200
LoginInterceptor:完成后处理(资源清理)
LogInterceptor:归档日志
  • 执行顺序:preHandle(Log → Login)→ Controller → postHandle(Login → Log)→ afterCompletion(Login → Log);
  • 倒序执行原因:HandlerExecutionChain 会记录已通过的拦截器索引,反向遍历执行后置 / 完成方法。
场景 2:请求未登录(token 无效)

plaintext

复制代码
LogInterceptor:记录请求日志 - /api/user
LoginInterceptor:未登录,中断请求
LogInterceptor:归档日志
  • 中断逻辑:LoginInterceptor 的 preHandle 返回 false,触发 triggerAfterCompletion(),仅执行已通过拦截器(LogInterceptor)的 afterCompletion;
  • 核心 Controller 未执行,postHandle 也不执行。

4. 责任链管理器:HandlerExecutionChain 核心逻辑

Spring MVC 通过 HandlerExecutionChain 管理拦截器链,核心源码简化如下:

java

运行

复制代码
public class HandlerExecutionChain {
    private final Object handler; // 最终的 Controller(Handler)
    private final List<HandlerInterceptor> interceptors = new ArrayList<>();
    private int interceptorIndex = -1; // 记录已通过的拦截器索引

    // 执行前置处理
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        for (int i = 0; i < interceptors.size(); i++) {
            HandlerInterceptor interceptor = interceptors.get(i);
            if (!interceptor.preHandle(request, response, handler)) {
                triggerAfterCompletion(request, response, null); // 中断,执行完成方法
                return false;
            }
            interceptorIndex = i; // 更新已通过索引
        }
        return true;
    }

    // 执行后置处理(倒序)
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
        for (int i = interceptors.size() - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors.get(i);
            interceptor.postHandle(request, response, handler, mv);
        }
    }

    // 执行完成后处理(倒序)
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {
        for (int i = interceptorIndex; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors.get(i);
            try {
                interceptor.afterCompletion(request, response, handler, ex);
            } catch (Throwable e) {
                // 忽略异常,保证所有拦截器的完成方法都执行
            }
        }
    }
}

三、场景 2:Servlet Filter 过滤器链(底层补充)

Filter 是 Servlet 规范的责任链,由 Tomcat 等容器管理,执行时机早于 HandlerInterceptor,核心流程:

plaintext

复制代码
Tomcat → ApplicationFilterChain → 
  Filter1.doFilter() → Filter2.doFilter() → ... → 
  DispatcherServlet.service() → HandlerInterceptor 链 → Controller

1. 代码示例(自定义 Filter)

java

运行

复制代码
// 字符编码过滤器
@Component
public class CharsetFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        System.out.println("CharsetFilter:设置编码为 UTF-8");
        chain.doFilter(request, response); // 传递给下一个 Filter
    }
}

2. 与 HandlerInterceptor 的核心区别

维度 HandlerInterceptor Filter
所属规范 Spring MVC 自定义 Servlet 规范
执行时机 DispatcherServlet 内部 DispatcherServlet 之前
拦截范围 仅拦截 Controller 请求 拦截所有 Web 请求(如静态资源)
中断方式 preHandle 返回 false 不调用 chain.doFilter ()
扩展能力 可访问 Spring 上下文(注入 Bean) 原生 Servlet 接口,无 Spring 依赖

四、Spring MVC 责任链的核心特点

1. 灵活性

  • 动态扩展 :新增拦截器只需实现 HandlerInterceptor 并注册,无需修改原有代码;
  • 顺序可控 :通过 order() 指定执行顺序,适配 "先日志、后校验" 等逻辑;
  • 路径匹配:可指定拦截 / 排除路径,精准控制拦截范围。

2. 中断性

  • 支持 "提前终止请求"(如登录校验失败直接返回 401),避免无效的核心逻辑执行;
  • 中断后仍保证已通过拦截器的 afterCompletion 执行,避免资源泄漏。

3. 解耦性

  • 请求发送者(客户端)无需知道处理逻辑;
  • 处理者(拦截器)无需知道完整链路,只需关注自身职责;
  • 核心业务(Controller)无需耦合通用逻辑(如日志、校验)。

五、典型应用场景

  1. 权限控制:登录校验、角色 / 数据权限校验;
  2. 日志监控:请求 / 响应日志、接口耗时统计;
  3. 跨域处理:添加 CORS 响应头;
  4. 参数预处理:请求参数解密、XSS 过滤;
  5. 资源清理:线程局部变量(ThreadLocal)清理、连接关闭。

六、总结

Spring MVC 是责任链模式的 "经典落地":

  1. 核心载体HandlerExecutionChain 管理 HandlerInterceptor 链,是 Spring MVC 原生责任链;
  2. 执行规则:preHandle 正序执行(可中断),postHandle/afterCompletion 倒序执行;
  3. 扩展价值:通过责任链解耦通用逻辑与核心业务,是 Spring MVC 灵活扩展的核心机制;
  4. 双层补充:Filter 链作为底层补充,覆盖更广泛的 Web 请求场景。
相关推荐
阿拉斯攀登2 小时前
设计模式:责任链模式(Spring Security)
设计模式·spring security·责任链模式
阿拉斯攀登2 小时前
设计模式:命令模式
设计模式·命令模式
阿闽ooo3 小时前
抽象工厂模式实战:用C++打造家具生产系统(附UML图与完整代码)
c++·设计模式·抽象工厂模式·uml
是店小二呀3 小时前
DanceGRPO+FLUX:多模态生成强化学习模型的高效
设计模式
明洞日记3 小时前
【设计模式手册022】抽象工厂模式 - 创建产品家族
java·设计模式·抽象工厂模式
阿拉斯攀登4 小时前
设计模式:命令模式(Spring MVC中的实践)
设计模式·springmvc·命令模式
明洞日记4 小时前
【设计模式手册021】代理模式 - 如何控制对象访问
设计模式·系统安全·代理模式
山沐与山4 小时前
【设计模式】Python策略模式:从入门到实战
python·设计模式·策略模式
阿拉斯攀登4 小时前
设计模式:责任链模式(mybatis数据权限实现)
设计模式·mybatis·责任链模式