什么是责任链模式?它解决什么问题?
简化版
责任链模式是把多个处理器按顺序串成一条链,请求沿着链依次传递,直到被某个处理器处理、拦截或传到链尾。它主要解决“请求发送者和具体处理者强耦合”的问题,让发送者不需要知道谁来处理,也方便动态增加、删除和调整处理步骤。
详细版
责任链模式属于行为型设计模式,核心思想是把一组可能处理同一请求的对象组织成链式结构。
一个请求进入系统后,不再直接调用某个固定处理器,而是交给链的入口。每个处理器只关心自己能不能处理:
- 能处理,就执行自己的逻辑;
- 不能处理,就把请求交给下一个处理器;
- 需要拦截时,可以不再向后传递;
- 需要叠加处理时,也可以处理完再继续传递。
典型场景包括 Servlet Filter、Spring MVC Interceptor、审批流、风控规则、日志过滤、权限校验、网关过滤器等。
责任链模式的价值是解耦请求发送者和处理者,让处理逻辑按步骤拆开,新增规则时尽量不修改主流程。但它也会让调用路径变得间接,需要注意链路顺序、异常处理和链尾兜底。
完整版教学
一、先理解“请求为什么不该直接找某个处理者”
假设有一个下单接口,请求进入后要做这些事情:
- 校验登录态;
- 校验参数;
- 校验库存;
- 校验风控;
- 记录访问日志;
- 执行业务下单。
最直接的写法是把所有逻辑堆在一个方法里:
public void createOrder(Request request) {
checkLogin(request);
checkParam(request);
checkStock(request);
checkRisk(request);
writeLog(request);
doCreateOrder(request);
}
这种写法短期很直观,但后续会越来越难维护。比如要新增“黑名单校验”,就要改主流程;要临时关闭某个校验,也要改主流程;不同业务线需要不同校验顺序时,主流程会充满条件分支。
责任链模式的做法是把每个步骤拆成独立处理器:
请求 -> 登录处理器 -> 参数处理器 -> 库存处理器 -> 风控处理器 -> 业务处理器
主流程只负责把请求交给链,不关心链上有多少节点,也不关心每个节点怎么处理。
二、责任链模式的本质是“处理机会的传递”
责任链模式不是简单地把代码拆成很多方法,它强调的是:每个处理器都有处理请求的机会,并且可以决定请求是否继续向后走。
一个处理器通常有三类行为:
- 放行:当前处理器检查通过,把请求交给下一个处理器;
- 拦截:当前处理器认为请求不合法,直接停止链路;
- 处理并继续:当前处理器做增强逻辑,比如打日志、埋点、包装上下文,然后继续传递。
这就是责任链和普通顺序调用的差别。普通顺序调用往往由一个中心方法控制每一步,而责任链把“是否继续”交给链上节点自己决定。
三、责任链模式解决的是发送者和处理者的耦合
没有责任链时,请求发送者通常要知道具体调用哪些处理逻辑:
Controller -> LoginChecker
Controller -> ParamChecker
Controller -> RiskChecker
Controller -> OrderService
使用责任链后,请求发送者只面向链入口:
Controller -> HandlerChain
至于链上有哪些处理器、顺序如何、是否短路,由链的组装逻辑负责。
这样带来的好处是,新增一个处理器时,通常只需要新增类并注册到链中,不需要大面积修改请求发送者。
四、责任链模式的关键不是“链”,而是“职责拆分”
很多同学会把责任链理解成“多个对象 next、next 地调用”,这只看到了结构,没有看到设计目标。
真正重要的是每个处理器的职责要足够单一。比如:
- 权限处理器只负责权限判断;
- 参数处理器只负责参数合法性;
- 限流处理器只负责流量控制;
- 日志处理器只负责记录请求与响应。
如果一个处理器里同时做权限、限流、日志和业务逻辑,那只是把大方法换了个位置,并没有获得责任链的好处。
五、责任链模式常见的两种处理方式
第一种是“命中即停止”。比如审批流中,经理审批通过后传给总监,总监审批通过后传给老板;某一层驳回时,流程停止。
第二种是“每个节点都处理”。比如 Web 过滤器,日志过滤器、鉴权过滤器、限流过滤器、跨域过滤器都可能处理同一个请求,只是每个节点处理的侧重点不同。
这两种都可以叫责任链,区别在于是否允许多个处理器共同处理同一请求。
六、处理机会如何沿链传递
请求依次经过 4 个处理器,前 2 个拒绝处理,第 3 个命中后结束,则实际调用 3 次,第 4 个不会执行。
request -> H1(pass) -> H2(pass) -> H3(handle/stop) -x-> H4
责任链是否正确,不能只看节点都被注册,还要看请求到底经过哪些节点、在哪里停止以及返回阶段如何展开。应把“继续、已处理、拒绝、系统失败”建成可区分的结果,并让日志能够还原一次请求的完整路径。
七、链路顺序、短路与可观测性
| 检查维度 | 应确认的内容 |
|---|---|
| 机制正确性 | 责任链解耦发送者和具体处理者,但链的组装、顺序和兜底仍需显式治理。 |
| 适用边界 | 若所有节点必须按固定顺序完成,更像管道;若节点之间有复杂回跳和补偿,则应考虑工作流。 |
| 测试证据 | 用记录型处理器覆盖全通过、主动短路、业务拒绝、节点异常和链尾无人处理 |
| 工程代价 | 重点测量链长、各节点耗时、最坏路径延迟和短路率,并确认动态装配不会产生重复节点或排序漂移 |
易错点:责任链解耦发送者和具体处理者,但链的组装、顺序和兜底仍需显式治理。
八、常见误区与追问
- 误区:责任链的重点是使用链表数据结构。 处理器可以存 next,也可以由 List 统一遍历;关键是请求的处理机会按规则传递。
- 误区:责任链一定会让每个处理器都执行。 纯链可能命中一个节点就结束,不纯链也可能因拒绝、异常或短路提前终止,必须由链契约定义。
- 追问:谁决定请求继续向后传递? 可以由处理器调用 next,也可以由 Chain 根据结果统一推进,两种设计要保持契约一致。
- 追问:责任链如何避免请求到链尾无人处理? 提供明确的默认处理器或链尾失败结果,并对未处理请求记录指标,不能静默成功。
- 追问:责任链上线后最少要记录哪些信息? 至少记录链版本、节点顺序、逐节点耗时与决策、短路位置、异常和最终处理结果。
九、加强记忆
责任链模式要记成“请求进链,节点判断,能处理就处理,需要继续就传递”。它解决的是请求发送者不应该依赖一堆具体处理者的问题,适合校验链、过滤器链、审批流、风控规则、网关拦截等场景;使用时重点关注职责拆分、链路顺序、短路条件和链尾兜底。