Handler接口必须统一返回boolean,因责任链依赖返回值决定是否继续传递;Request需为可变POJO以支持动态字段扩展;应通过List/Deque运行时组装链并注入traceId实现全链路日志追踪。

Handler 接口设计必须统一返回 boolean 而非 void
很多初学者把 handle() 设成 void,结果发现请求“静默消失”——链断了却没报错。责任链的核心契约是:每个 Handler 决定是否继续传递,这只能靠返回值显式表达。
典型错误现象:handleRequest() 里调用 next.handle(request) 前没判断返回值,或自己处理完直接 return,忘了调用 next;更隐蔽的是,某个 Handler 返回 false 后,后续 Handler 全被跳过,但没人意识到这是设计意图还是 bug。
-
Handler接口必须定义boolean handle(Request request) - 每个实现类在处理完逻辑后,明确 return
true(终止)或false(继续) - 调用链中必须检查前序返回值:
if (!current.handle(req)) { next.handle(req); } - 避免在
handle()内吞掉异常,否则链会在某处静默中断
链的构建不能依赖 new 多个 Handler 实例硬编码
写死 new ApproverA().setNext(new ApproverB().setNext(new ApproverC())) 看似简单,实际导致配置僵化、测试困难、无法动态增删节点。尤其审批流常需按组织架构或业务类型切换路径,硬编码会让每次变更都得改代码、发版。
常见错误是把链初始化塞进 Service 初始化块里,结果 Spring 容器启动时报 NullPointerException —— 因为 setNext() 时某些 Handler 还没被注入。
立即学习“Java免费学习笔记(深入)”;
- 用
List<Handler>或Deque<Handler>存储处理器,运行时组装 - Spring 场景下,通过
@Autowired List<Handler> handlers自动收集所有实现类,再按@Order或自定义注解排序 - 避免在构造函数里调用
setNext(),改用@PostConstruct或配置类统一 build 链 - 链对象本身建议封装为
ApprovalChain类,暴露execute(Request)方法,隐藏内部遍历逻辑
Request 对象必须可变且支持字段扩展,不能用 final 字段封装
审批过程中,不同 Handler 往往要补充信息:比如财务岗加 budgetApproved,法务岗加 contractReviewed。如果 Request 是不可变对象(如全 final 字段 + builder 构造),每加一个字段就得改类、重编译,链上各 Handler 还得强转类型才能读写,彻底破坏开闭原则。
典型错误现象:ApproverB 想读 ApproverA 设置的字段,结果发现 Request 没提供 setter,只好新增子类 FinanceRequest extends Request,导致链上类型判断泛滥,instanceof 满天飞。
-
Request应设计为普通 POJO,字段 public 或带标准 getter/setter - 关键字段命名需带业务上下文,如
approvedByFinance而非模糊的flag1 - 避免用 Map<String, Object> 存所有字段——类型丢失,IDE 无法提示,单元测试难写
- 如需隔离字段可见性,可用包级私有字段 + 同包内专用 Handler 访问,而非靠继承
调试时看不到请求在哪一环卡住?加统一 traceId 和日志钩子
生产环境出问题,最头疼的是“这个审批单到底走到哪了?”——日志里只有零散的 “ApproverA start”、“ApproverC finish”,中间 B 没打印,是因为没执行?执行失败?还是被跳过了?没有贯穿链的日志标识,排查效率极低。
容易被忽略的是:每个 Handler 的日志打在不同线程(比如用了异步审批),或被不同 SLF4J 绑定覆盖,导致 traceId 断裂。
- 在
Request中加String traceId字段,入口处生成并透传 - 每个
Handler.handle()开头打日志:log.debug("[{}] Entering {} with status {}", req.getTraceId(), this.getClass().getSimpleName(), req.getStatus()) - Spring 环境下,用
MDC.put("traceId", req.getTraceId())确保异步线程也能继承 - 链执行器(
ApprovalChain.execute())捕获所有未处理异常,并在 finally 块记录 “chain terminated at [class] due to [ex]”
责任链不是写完就完事的模式,它的复杂点永远在边界:谁该终止、谁该放行、状态怎么共享、出错了怎么定位。这些地方不提前想清楚,后期加监控、改流程、查问题,全是坑。











