
走进 Gang of Four 设计模式责任链模式说明不仅告诉你怎么用更告诉你为什么这样设计前置知识Java 面向对象基础抽象类、多态、链表结构 设计模式分类总览GoF 23 种设计模式可按两个维度交叉分类类/对象处理方式 ×创建型/结构型/行为型目的。维度创建型Creational结构型Structural行为型Behavioral类Class通过继承复用Factory Method工厂方法Adapter类适配器Interpreter解释器Template Method模板方法对象Object通过组合/聚合复用Abstract Factory抽象工厂Builder建造者Prototype原型Singleton单例Adapter对象适配器Bridge桥接Composite组合Decorator装饰Facade外观Flyweight享元Proxy代理Chain of Resp.责任链Command命令Iterator迭代器Mediator中介者Memento备忘录Observer观察者State状态Strategy策略Visitor访问者 目录模式概述 · 15 问深度分析框架源码实战分析Servlet Filter 责任链源码拆解MyBatis InterceptorChain 源码拆解Spring Security FilterChain 源码拆解深度追问Why / How / Trade-off / Evolution / Modern Practice总结1. 模式概述 · 15 问深度分析Q1: 为什么需要这个模式它解决了什么问题在软件开发中经常会出现一个请求需要经过多个环节处理的场景例如权限校验 → 数据脱敏 → 日志记录 → 业务核心处理。如果让调用者直接知道所有处理节点调用者就必须耦合所有的处理逻辑和流转规则。责任链模式正是为了解决请求发送者与多个请求处理者之间的紧耦合问题。它将处理者连成一条链让请求沿着这条链传递直到有对象处理它为止或者每个节点都参与处理。核心矛盾确定性的调用流与不确定的处理者之间的矛盾。Q2: 如果不用这个模式会有什么缺陷不用责任链模式时通常会采用硬编码的if-else或switch-case逻辑。// ❌ 硬编码——新增处理者需要改源码publicvoidhandle(Requestrequest){if(request.hasPermission()){// 权限校验通过if(request.needsLogging()){log.info(处理请求: request);}// 核心业务处理process(request);// 发送通知notifyUser(request);}}缺陷违反开闭原则新增或删除一个处理环节时必须修改核心类的代码。硬编码流程处理顺序和逻辑在编译期就固定了无法在运行时动态调整。代码臃肿一个方法中充斥着多个处理阶段难以维护和测试。Q3: 核心思想是什么一句话如何概括核心思想将请求沿着处理者链传递每个处理者决定是否处理或传递给下一个。一句话概括为请求创建一条接收者对象的链沿着这条链传递请求直到有一个处理者处理它。Q4: 包含哪些角色每个角色的职责是什么责任链模式包含以下 3 个核心角色Handler抽象处理者定义处理请求的接口通常包含一个处理方法和一个指向下一个处理者的引用。ConcreteHandler具体处理者实现 Handler 接口判断是否能处理当前请求。如果能则自己处理如果不能则调用nextHandler.handle()传递给下一个。Client客户端组装责任链向链头发送请求。Q5: 它们之间如何协作调用流程是怎样的标准调用流程组装阶段客户端将处理者 A、B、C 串联成链A → B → C。请求发起客户端向链头A发送请求。流转处理A 处理或传递给 BB 处理或传递给 CC 处理或终止结果返回请求被某节点处理后返回结果或到达链尾未被处理。Q6: 为什么要这样设计每个角色存在的意义是什么Handler 的意义定义了统一的处理接口让客户端无需关心链中的具体处理者。ConcreteHandler 的意义每个处理者只关心自己能处理的那部分逻辑实现了关注点分离。Client 的意义负责组装链——可以在运行时动态决定链的组成和顺序。Q7: 为什么使用接口、抽象类、组合而不是其他方式为什么用抽象类因为 Handler 需要持有下一个处理者的引用并且通常有一个默认的转发逻辑if (next ! null) next.handle()抽象类可以统一实现这个逻辑。为什么用组合链表结构链表可以在运行时动态添加、删除或调整处理者的顺序。如果用数组或列表硬编码灵活性大打折扣。Q8: 这种设计符合哪些面向对象原则开闭原则OCP极佳。新增处理者只需新建一个 ConcreteHandler 并接入链中无需修改任何现有代码。单一职责原则SRP每个具体处理者只负责一种处理逻辑如权限校验、日志记录职责非常单一。依赖倒置原则DIP客户端和具体处理者都依赖抽象的 Handler 接口。Q9: 它有哪些优点和局限性会带来哪些额外成本优点降低耦合请求发送者和接收者之间完全解耦。动态灵活可以在运行时动态新增、删除或调整处理者顺序。符合单一职责每个处理者只关心自己的逻辑。局限性/额外成本请求可能不被处理如果链的末端没有兜底处理者请求可能被丢弃。调试困难运行时调用链不直观需要跟踪链的流转。性能影响请求可能经过多个处理者才被处理链太长影响性能。Q10: 它最适合哪些场景哪些场景不应该使用最适合的场景有多个对象可以处理请求具体由谁处理由运行时决定。需要动态指定或调整处理者链的顺序如按配置决定 Filter 顺序。向多个对象中的一个提交请求不想明确指定接收者。不应该使用的场景处理者只有 1-2 个且固定不变时用简单的 if-else 更轻量。请求的处理路径需要在编译期就严格确定时。Q11: 它与容易混淆的其他设计模式有什么区别和联系与装饰器模式的区别对比维度责任链模式装饰器模式请求流向链式传递可能由链中某个节点终止层层包装每个装饰器都执行处理方式请求最多被一个处理者处理或全部处理每个装饰器都增强功能结构链表结构嵌套结构典型应用FilterChain、MyBatis PluginJava IO Stream与观察者模式的区别观察者模式是 Subject 主动广播给所有 Observer一对多。责任链模式是请求沿着链传递直到有 Handler 处理它一对一。Q12: 框架中有哪些典型应用Tomcat / Servlet 中的FilterChain在请求到达 Servlet 之前经过一系列 Filter层层过滤 Request/Response。MyBatis 插件机制InterceptorChain多个 MyBatis 插件构成责任链依次拦截 SQL 执行。Spring Security 中的SecurityFilterChain一组安全过滤器链按顺序对 HTTP 请求进行安全处理。Netty 的ChannelPipeline出站/入站处理器的责任链。Q13: 如何从零实现一个最小可运行版本以日志级别过滤器为例// 1. Handler 抽象处理者publicabstractclassAbstractLogger{publicstaticintINFO1;publicstaticintDEBUG2;publicstaticintERROR3;protectedintlevel;protectedAbstractLoggernextLogger;// 指向下一个处理者publicvoidsetNextLogger(AbstractLoggernextLogger){this.nextLoggernextLogger;}publicvoidlogMessage(intlevel,Stringmessage){if(this.levellevel){write(message);}if(nextLogger!null){nextLogger.logMessage(level,message);// 传递给下一个}}abstractprotectedvoidwrite(Stringmessage);}// 2. ConcreteHandler 具体处理者publicclassConsoleLoggerextendsAbstractLogger{publicConsoleLogger(intlevel){this.levellevel;}Overrideprotectedvoidwrite(Stringmessage){System.out.println(Standard Console::Logger: message);}}publicclassFileLoggerextendsAbstractLogger{publicFileLogger(intlevel){this.levellevel;}Overrideprotectedvoidwrite(Stringmessage){System.out.println(File::Logger: message);}}publicclassErrorLoggerextendsAbstractLogger{publicErrorLogger(intlevel){this.levellevel;}Overrideprotectedvoidwrite(Stringmessage){System.out.println(Error Console::Logger: message);}}// 3. Client 组装链publicclassChainPatternDemo{privatestaticAbstractLoggergetChainOfLoggers(){AbstractLoggererrorLoggernewErrorLogger(AbstractLogger.ERROR);AbstractLoggerfileLoggernewFileLogger(AbstractLogger.DEBUG);AbstractLoggerconsoleLoggernewConsoleLogger(AbstractLogger.INFO);errorLogger.setNextLogger(fileLogger);fileLogger.setNextLogger(consoleLogger);returnerrorLogger;// 返回链头}publicstaticvoidmain(String[]args){AbstractLoggerloggerChaingetChainOfLoggers();loggerChain.logMessage(AbstractLogger.INFO,This is an information.);loggerChain.logMessage(AbstractLogger.DEBUG,This is a debug level information.);loggerChain.logMessage(AbstractLogger.ERROR,This is an error information.);}}Q14: 如何在现有项目中识别可以使用该模式的地方如果出现以下信号可以考虑重构为责任链模式代码中频繁出现链式的if-else if-else if或switch且每个分支的处理逻辑不同。一个方法中包含了多个阶段的处理权限校验 → 日志 → 核心业务 → 通知且阶段可能经常增减。需要根据配置或运行时条件动态决定请求的处理路径。Q15: 如果重新设计这个模式在今天会有哪些改进在现代 JavaJava 8中函数式接口Function、Consumer可以替代部分责任链节点消除处理者类的数量// 函数式责任链publicclassFunctionalChain{privatefinalListConsumerRequesthandlersnewArrayList();publicFunctionalChainaddHandler(ConsumerRequesthandler){handlers.add(handler);returnthis;}publicvoidexecute(Requestrequest){for(ConsumerRequesthandler:handlers){handler.accept(request);}}}// 使用时无需创建 Handler 子类newFunctionalChain().addHandler(req-System.out.println(权限校验)).addHandler(req-System.out.println(日志记录)).addHandler(req-System.out.println(核心业务)).execute(request);2. 框架源码实战分析2.1 Servlet FilterChain 源码拆解Servlet 的 FilterChain 是责任链模式最典型的工业级应用结合了模板方法模式。场景背景在 HTTP 请求到达 Servlet 之前需要经过一系列 Filter如编码过滤、登录校验、XSS 防护。这些 Filter 的顺序通常由web.xml或注解配置决定可以在运行时动态调整。核心源码// Tomcat 中 FilterChain 的核心实现publicclassApplicationFilterChainimplementsFilterChain{// 内部维护过滤器数组privateApplicationFilterConfig[]filtersnewApplicationFilterConfig[0];privateintpos0;// 当前执行位置指针privateServletservlet;// 目标 ServletOverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse){if(posfilters.length){// 取出当前过滤器按顺序ApplicationFilterConfigfilterConfigfilters[pos];FilterfilterfilterConfig.getFilter();// 将自身this作为 FilterChain 传入// 这样在 Filter 中可以调用 chain.doFilter() 继续传递filter.doFilter(request,response,this);}else{// 所有过滤器执行完毕调用目标 Servletservlet.service(request,response);}}}// 开发者自定义 FilterWebFilter(/*)publicclassLoggingFilterimplementsFilter{publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{// 前置处理System.out.println(请求前 - System.currentTimeMillis());// 调用下一个 Filter关键chain.doFilter(request,response);// 后置处理System.out.println(请求后 - System.currentTimeMillis());}}为什么这样设计每个 Filter 通过chain.doFilter()将控制权交回给ApplicationFilterChainchain 再调用下一个 Filter 或最终的 Servlet。这种设计实现了过滤器链的职责链模式同时每个 Filter 可以在调用前后添加逻辑又体现了模板方法模式。2.2 MyBatis InterceptorChain 源码拆解MyBatis 的插件机制本质上是责任链模式的应用。场景背景MyBatis 允许开发者编写插件来拦截四大核心对象的方法调用Executor、StatementHandler、ParameterHandler、ResultSetHandler。多个插件构成一条责任链依次执行拦截逻辑。核心源码// MyBatis 中的拦截器链publicclassInterceptorChain{privatefinalListInterceptorinterceptorsnewArrayList();// 注册拦截器publicObjectpluginAll(Objecttarget){// 遍历所有拦截器依次包装目标对象// 注意这是责任链模式的一种变体——先注册的在内层for(Interceptorinterceptor:interceptors){targetinterceptor.plugin(target);}returntarget;}publicvoidaddInterceptor(Interceptorinterceptor){interceptors.add(interceptor);}}// 自定义 MyBatis 插件Intercepts({Signature(typeExecutor.class,methodquery,args{MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})})publicclassExamplePluginimplementsInterceptor{OverridepublicObjectintercept(Invocationinvocation)throwsThrowable{// 前置处理System.out.println(SQL 执行前);// 调用下一个拦截器或目标方法Objectresultinvocation.proceed();// 后置处理System.out.println(SQL 执行后);returnresult;}OverridepublicObjectplugin(Objecttarget){// 用 Plugin.wrap 来生成代理returnPlugin.wrap(target,this);}}2.3 Spring Security FilterChain 源码拆解Spring Security 的安全过滤机制是责任链模式在企业级安全框架中的顶级应用。场景背景Spring Security 通过一组SecurityFilterChain来保护 Web 应用。每个 FilterChain 包含多个安全过滤器如UsernamePasswordAuthenticationFilter、ExceptionTranslationFilter、FilterSecurityInterceptor按照特定顺序执行。核心源码// Spring Security 的过滤器链管理器publicclassFilterChainProxyextendsGenericFilterBean{// 持有多个 SecurityFilterChain责任链数组privateListSecurityFilterChainfilterChains;OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{// 遍历所有 SecurityFilterChain找到匹配当前请求的第一个for(SecurityFilterChainfilterChain:filterChains){if(filterChain.matches(request)){// 执行匹配的 SecurityFilterChain 中的所有 FilterfilterChain.getFilters().doFilter(request,response,chain);return;}}// 没有匹配的 chain直接放行chain.doFilter(request,response);}}// 配置多个安全过滤链ConfigurationEnableWebSecuritypublicclassSecurityConfig{BeanpublicSecurityFilterChainapiFilterChain(HttpSecurityhttp)throwsException{http.securityMatcher(/api/**)// 只匹配 /api/ 路径.authorizeHttpRequests(auth-auth.anyRequest().authenticated()).httpBasic(Customizer.withDefaults());returnhttp.build();}BeanpublicSecurityFilterChainwebFilterChain(HttpSecurityhttp)throwsException{http.securityMatcher(/web/**)// 匹配 /web/ 路径.authorizeHttpRequests(auth-auth.requestMatchers(/login).permitAll().anyRequest().authenticated()).formLogin(Customizer.withDefaults());returnhttp.build();}}三个框架对比框架组装方式传递机制终止条件Servlet Filterweb.xml/注解顺序chain.doFilter()手动传递全部执行完 → ServletMyBatis Plugininterceptors.add()注册invocation.proceed()内层外层的包装全部执行完 → 目标方法Spring Security代码配置 chain 顺序自动匹配第一个 chain 后执行匹配到的 chain 执行完或放行3. 深度追问3.1 Why为什么解决哪个根本矛盾责任链模式解决的根本矛盾是确定性的调用流与不确定的处理者之间的矛盾。在复杂的业务系统中请求需要经过多个处理环节但哪些处理环节是必需的、它们的顺序如何这些信息在编译期往往无法确定。如果使用硬编码的 if-else每次调整处理环节都需要修改核心代码。责任链模式通过将处理者串联成链将谁处理的决定权从编译期延后到运行期。3.2 How怎么做对象关系与交互机制责任链模式的核心机制可以总结为隐式配置、多态传递、控制权让渡。隐式配置链的组装在客户端完成Handler 不知道链的全貌。多态传递每个 Handler 持有抽象 Handler 的引用调用next.handle()时利用多态。控制权让渡每个 Handler 执行完毕后可以决定是终止处理还是继续传递。典型的链式调用伪代码abstractclassHandler{protectedHandlernext;publicvoidhandle(Requestreq){if(canHandle(req)){doHandle(req);}elseif(next!null){next.handle(req);// 控制权让渡}}}3.3 Trade-off代价牺牲了什么换取什么它牺牲了付出代价请求处理的确定性请求可能经过整条链都没有处理者能处理如果没有兜底处理者。调试的直观性运行时调用链不直观需要跟踪next.handle()的传递过程。性能损耗链越长请求经过的处理者越多性能损耗越大。它换取了获得收益处理者之间的完全解耦每个处理者只关心自己的逻辑不知道其他处理者的存在。运行时动态调整可以随意新增、删除或重排处理者。单一职责每个处理者的职责非常单一清晰。3.4 Evolution演化它是如何从简单设计逐步演化而来的硬编码 if-else起点所有处理逻辑写在一个方法里最直接但也最混乱。策略模式动态选择单个处理者但只能选一个不能链式传递。责任链模式经典链式传递多个处理者依次尝试直到有人处理。责任链 模板方法现代变体如 Servlet Filter在链式传递的基础上每个 Filter 可以通过chain.doFilter()在前后添加逻辑结合了模板方法模式。3.5 Modern Practice现代实践它在今天被取代了吗结论责任链模式不仅没有被取代反而在现代框架中成为了核心基础设施。Servlet FilterChain仍然是 Java Web 应用的基础。Spring Security的SecurityFilterChain是安全框架的基石。MyBatis Plugin基于责任链实现插件机制。Netty的ChannelPipeline是高性能网络框架的核心。在函数式编程中可以使用Function链式组合实现轻量级责任链FunctionRequest,Responsehandlerrequest.andThen(this::validate).andThen(this::process).andThen(this::respond);但这种方式的灵活性不如经典责任链模式不能中断传递说明经典责任链在处理有条件的传递时仍有不可替代的优势。4. 总结核心要点责任链模式将请求沿着处理者链传递直到有处理者处理它核心结构是链表每个处理者持有下一个处理者的引用与装饰器的区别责任链是链式传递可能终止装饰器是层层嵌套全部执行典型工业应用Servlet Filter、MyBatis Interceptor、Spring Security FilterChain、Netty ChannelPipeline一句话责任链模式就是击鼓传花——请求沿着链传递直到有人能处理它。5. 参考文献[1] GAMMA E, HELM R, JOHNSON R, et al. Design Patterns: Elements of Reusable Object-Oriented Software[M]. Boston: Addison-Wesley, 1994.[2] Apache Tomcat. Tomcat FilterChain Source[EB/OL]. https://tomcat.apache.org/, 2024.[3] MyBatis. MyBatis 3 Plugin[EB/OL]. https://mybatis.org/mybatis-3/configuration.html#plugins, 2024.[4] SPRING. Spring Security Architecture[EB/OL]. https://docs.spring.io/spring-security/, 2024.