责任链模式实战应用:从理论到生产实践
在企业级开发中,我们经常遇到这样的场景:一个请求需要多个对象依次处理,每个对象根据自身职责决定是否处理或传递下去。这时候,责任链模式就能派上大用场。
一、什么是责任链模式
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它允许多个对象按顺序处理请求,每个对象都有机会处理请求或将其传递给链中的下一个对象。
1.1 核心思想
为请求创建一个接收者对象链,让请求沿着链传递,直到有对象处理它为止。
1.2 系统架构图

从上图可以看出,责任链模式包含以下几个核心层次:
- 请求层:封装需要在链中传递的数据
- 处理器接口层:定义处理器的标准行为
- 抽象处理器层:实现责任链的基本逻辑
- 具体处理器层:实现具体的业务处理逻辑
- 客户端层:创建链并发起请求
1.3 为什么要使用责任链模式
假设你在开发一个企业OA系统的审批模块:
scss
传统写法:
if (amount <= 1000) {
teamLeader.approve();
} else if (amount <= 5000) {
teamLeader.approve();
manager.approve();
} else if (amount <= 20000) {
teamLeader.approve();
manager.approve();
director.approve();
} else {
// 一大堆代码逻辑
.........
.........
}
这种写法的问题显而易见:
- 代码耦合严重,难以维护
- 新增审批人需要修改大量代码
- 不符合开闭原则
- 逻辑复杂,容易出错
而使用责任链模式后:
scss
✓ 责任链写法:
teamLeader.setNext(manager).setNext(director).setNext(vp);
teamLeader.handle(request);
简洁、优雅、易扩展!
二、责任链模式核心设计
2.1 类图结构

核心角色:
- Handler(处理器接口) :定义处理请求的接口
- AbstractHandler(抽象处理器) :实现责任链的基本逻辑,维护对下一个处理器的引用
- ConcreteHandler(具体处理器) :具体的业务逻辑实现
2.2 工作原理

工作流程:
- 客户端将请求发送给责任链的第一个处理器
- 每个处理器判断自己是否能处理该请求
- 如果能处理则处理并返回(可选择是否继续传递)
- 如果不能处理则传递给下一个处理器
- 直到有处理器处理或到达链尾
三、实战案例一:企业审批流程(Apache Commons Chain实现)
3.1 需求分析
某企业的费用审批规则如下:
| 金额范围 | 审批人 |
|---|---|
| 0-1000元 | 组长 |
| 1000-5000元 | 经理 |
| 5000-20000元 | 总监 |
| 20000元以上 | VP |
| 超过50000元 | 拒绝(需董事会审批) |

3.2 添加Maven依赖
xml
<dependency>
<groupId>commons-chain</groupId>
<artifactId>commons-chain</artifactId>
<version>1.2</version>
</dependency>
3.3 代码实现
定义审批上下文:
typescript
public class ApprovalContext extends ContextBase {
public String getApplicant() {
return (String) get("applicant");
}
public void setApplicant(String applicant) {
put("applicant", applicant);
}
public Double getAmount() {
return (Double) get("amount");
}
public void setAmount(Double amount) {
put("amount", amount);
}
public String getReason() {
return (String) get("reason");
}
public void setReason(String reason) {
put("reason", reason);
}
public boolean isApproved() {
Boolean approved = (Boolean) get("approved");
return approved != null && approved;
}
public void setApproved(boolean approved) {
put("approved", approved);
}
public String getApprover() {
return (String) get("approver");
}
public void setApprover(String approver) {
put("approver", approver);
}
}
定义审批命令(Command):
java
// 组长审批命令
public class TeamLeaderCommand implements Command {
private static final double MAX_AMOUNT = 1000;
private final String teamLeaderName;
public TeamLeaderCommand(String teamLeaderName) {
this.teamLeaderName = teamLeaderName;
}
@Override
public boolean execute(Context context) throws Exception {
ApprovalContext ctx = (ApprovalContext) context;
Double amount = ctx.getAmount();
System.out.println("【组长-" + teamLeaderName + "】审批:" + ctx.getApplicant() + "申请" + amount + "元");
if (amount <= MAX_AMOUNT) {
ctx.setApproved(true);
ctx.setApprover(teamLeaderName);
System.out.println(" ✓ 组长审批通过");
return false; // 处理完成,终止链条
}
System.out.println(" → 提交上级审批");
return true; // 继续执行下一个命令
}
}
// 经理审批命令
public class ManagerCommand implements Command {
private static final double MIN_AMOUNT = 1000;
private static final double MAX_AMOUNT = 5000;
private final String managerName;
public ManagerCommand(String managerName) {
this.managerName = managerName;
}
@Override
public boolean execute(Context context) throws Exception {
ApprovalContext ctx = (ApprovalContext) context;
Double amount = ctx.getAmount();
System.out.println("【经理-" + managerName + "】审批:" + ctx.getApplicant() + "申请" + amount + "元");
if (amount > MIN_AMOUNT && amount <= MAX_AMOUNT) {
ctx.setApproved(true);
ctx.setApprover(managerName);
System.out.println(" ✓ 经理审批通过");
return false; // 处理完成,终止链条
}
System.out.println(" → 提交上级审批");
return true; // 继续执行下一个命令
}
}
// 总监审批命令
public class DirectorCommand implements Command {
private static final double MIN_AMOUNT = 5000;
private static final double MAX_AMOUNT = 20000;
private final String directorName;
public DirectorCommand(String directorName) {
this.directorName = directorName;
}
@Override
public boolean execute(Context context) throws Exception {
ApprovalContext ctx = (ApprovalContext) context;
Double amount = ctx.getAmount();
System.out.println("【总监-" + directorName + "】审批:" + ctx.getApplicant() + "申请" + amount + "元");
if (amount > MIN_AMOUNT && amount <= MAX_AMOUNT) {
ctx.setApproved(true);
ctx.setApprover(directorName);
System.out.println(" ✓ 总监审批通过");
return false; // 处理完成,终止链条
}
System.out.println(" → 提交上级审批");
return true; // 继续执行下一个命令
}
}
// VP审批命令
public class VPCommand implements Command {
private static final double MIN_AMOUNT = 20000;
private static final double REJECT_AMOUNT = 50000;
private final String vpName;
public VPCommand(String vpName) {
this.vpName = vpName;
}
@Override
public boolean execute(Context context) throws Exception {
ApprovalContext ctx = (ApprovalContext) context;
Double amount = ctx.getAmount();
System.out.println("【VP-" + vpName + "】审批:" + ctx.getApplicant() + "申请" + amount + "元");
if (amount > REJECT_AMOUNT) {
ctx.setApproved(false);
ctx.setApprover("系统");
System.out.println(" ✗ 金额过大,需董事会审批");
return false; // 处理完成,终止链条
}
ctx.setApproved(true);
ctx.setApprover(vpName);
System.out.println(" ✓ VP审批通过");
return false; // 处理完成,终止链条
}
}
配置审批责任链:
arduino
public class ApprovalChainConfig {
private final String teamLeaderName;
private final String managerName;
private final String directorName;
private final String vpName;
public ApprovalChainConfig(String teamLeaderName, String managerName,
String directorName, String vpName) {
this.teamLeaderName = teamLeaderName;
this.managerName = managerName;
this.directorName = directorName;
this.vpName = vpName;
}
public ChainBase createApprovalChain() {
ChainBase chain = new ChainBase();
// 按顺序添加审批命令:组长 -> 经理 -> 总监 -> VP
chain.addCommand(new TeamLeaderCommand(teamLeaderName));
chain.addCommand(new ManagerCommand(managerName));
chain.addCommand(new DirectorCommand(directorName));
chain.addCommand(new VPCommand(vpName));
return chain;
}
}
使用示例:
csharp
public class ApprovalChainDemo {
public static void main(String[] args) throws Exception {
System.out.println("========================================");
System.out.println(" Apache Commons Chain - 企业审批流程");
System.out.println("========================================\n");
// 创建审批责任链
ApprovalChainConfig config = new ApprovalChainConfig("张三", "李四", "王五", "赵六");
ChainBase approvalChain = config.createApprovalChain();
// 测试场景1:500元 - 组长审批
testApproval(approvalChain, "小明", "技术部", 500.0, "购买办公文具");
System.out.println();
// 测试场景2:3000元 - 经理审批
testApproval(approvalChain, "小红", "市场部", 3000.0, "客户招待费");
System.out.println();
// 测试场景3:10000元 - 总监审批
testApproval(approvalChain, "小刚", "销售部", 10000.0, "市场推广费用");
System.out.println();
// 测试场景4:30000元 - VP审批
testApproval(approvalChain, "小丽", "研发部", 30000.0, "服务器采购");
System.out.println();
// 测试场景5:60000元 - 拒绝
testApproval(approvalChain, "小华", "运营部", 60000.0, "年度团建费用");
}
private static void testApproval(ChainBase chain, String applicant,
String department, double amount, String reason) throws Exception {
System.out.println("----------------------------------------");
System.out.println("测试场景:" + applicant + "申请" + amount + "元");
System.out.println("----------------------------------------");
ApprovalContext context = new ApprovalContext();
context.setApplicant(applicant);
context.setAmount(amount);
context.setReason(reason);
chain.execute(context);
System.out.println("\n最终结果:");
System.out.println(" 状态:" + (context.isApproved() ? "✓ 通过" : "✗ 拒绝"));
System.out.println(" 审批人:" + context.getApprover());
}
}
3.4 Apache Commons Chain的优势
通过使用Apache Commons Chain实现审批流程,带来以下优势:
- 配置灵活:可以通过XML配置或编程方式动态调整审批链
- 职责分离:每个Command只关注自己的审批逻辑
- 易于扩展:新增审批人只需添加新的Command
- 统一上下文:Context在整个链条中传递数据
- 标准化:使用成熟的开源框架,代码更规范
四、实战案例二:日志处理系统(Apache Commons Chain实现)
4.1 场景描述
在日志系统中,不同级别的日志需要不同的处理方式:
- DEBUG/INFO:输出到控制台
- INFO及以上:写入文件
- ERROR:发送告警邮件

4.2 代码实现
定义日志上下文:
csharp
public class LogContext extends ContextBase {
public LogLevel getLevel() {
return (LogLevel) get("level");
}
public void setLevel(LogLevel level) {
put("level", level);
}
public String getMessage() {
return (String) get("message");
}
public void setMessage(String message) {
put("message", message);
}
public LocalDateTime getTimestamp() {
return (LocalDateTime) get("timestamp");
}
public void setTimestamp(LocalDateTime timestamp) {
put("timestamp", timestamp);
}
}
enum LogLevel {
DEBUG(1), INFO(2), WARN(3), ERROR(4);
private final int level;
LogLevel(int level) { this.level = level; }
public int getLevel() { return level; }
}
定义日志处理命令:
java
// 控制台日志命令
public class ConsoleLogCommand implements Command {
private final LogLevel minLevel;
public ConsoleLogCommand(LogLevel minLevel) {
this.minLevel = minLevel;
}
@Override
public boolean execute(Context context) throws Exception {
LogContext ctx = (LogContext) context;
if (ctx.getLevel().getLevel() >= minLevel.getLevel()) {
String logMessage = String.format("[%s] %s %s",
ctx.getLevel(),
ctx.getTimestamp(),
ctx.getMessage());
System.out.println(logMessage);
}
return true; // 继续执行下一个命令
}
}
// 文件日志命令
public class FileLogCommand implements Command {
private final LogLevel minLevel;
private final String filePath;
public FileLogCommand(LogLevel minLevel, String filePath) {
this.minLevel = minLevel;
this.filePath = filePath;
}
@Override
public boolean execute(Context context) throws Exception {
LogContext ctx = (LogContext) context;
if (ctx.getLevel().getLevel() >= minLevel.getLevel()) {
try (FileWriter writer = new FileWriter(filePath, true)) {
String logEntry = String.format("[%s] %s %s%n",
ctx.getLevel(),
ctx.getTimestamp(),
ctx.getMessage());
writer.write(logEntry);
System.out.println(">>> 已写入文件:" + filePath);
}
}
return true; // 继续执行下一个命令
}
}
// 错误告警命令
public class ErrorAlertCommand implements Command {
private final String alertEmail;
public ErrorAlertCommand(String alertEmail) {
this.alertEmail = alertEmail;
}
@Override
public boolean execute(Context context) throws Exception {
LogContext ctx = (LogContext) context;
if (ctx.getLevel() == LogLevel.ERROR) {
// 模拟发送告警邮件
System.out.println("!!! 发送告警邮件到:" + alertEmail);
System.out.println("!!! 错误内容:" + ctx.getMessage());
}
return true; // 继续执行下一个命令
}
}
配置日志处理链:
csharp
public class LoggingChainConfig {
public ChainBase createLoggingChain() {
ChainBase chain = new ChainBase();
// 按顺序添加日志处理命令
chain.addCommand(new ConsoleLogCommand(LogLevel.DEBUG));
chain.addCommand(new FileLogCommand(LogLevel.INFO, "application.log"));
chain.addCommand(new ErrorAlertCommand("admin@example.com"));
return chain;
}
}
使用示例:
scss
public class LoggingChainDemo {
public static void main(String[] args) throws Exception {
System.out.println("========================================");
System.out.println(" Apache Commons Chain - 日志处理系统");
System.out.println("========================================\n");
LoggingChainConfig config = new LoggingChainConfig();
ChainBase loggingChain = config.createLoggingChain();
// 测试不同级别的日志
testLog(loggingChain, LogLevel.DEBUG, "系统启动中...");
System.out.println();
testLog(loggingChain, LogLevel.INFO, "用户登录成功");
System.out.println();
testLog(loggingChain, LogLevel.WARN, "内存使用率达到80%");
System.out.println();
testLog(loggingChain, LogLevel.ERROR, "数据库连接失败");
}
private static void testLog(ChainBase chain, LogLevel level, String message) throws Exception {
LogContext context = new LogContext();
context.setLevel(level);
context.setMessage(message);
context.setTimestamp(LocalDateTime.now());
chain.execute(context);
}
}
五、实战案例三:订单创建的多重校验(Apache Commons Chain实现)
5.1 场景描述
在电商系统中,创建订单时需要进行多重校验,包括:
- 用户状态校验(是否被禁用)
- 商品库存校验(是否充足)
- 优惠券有效性校验(是否可用)
- 金额限制校验(是否超限)
- 风控规则校验(是否可疑订单)
5.2 代码实现
定义订单上下文:
typescript
public class OrderContext extends ContextBase {
public Long getUserId() { return (Long) get("userId"); }
public void setUserId(Long userId) { put("userId", userId); }
public Long getProductId() { return (Long) get("productId"); }
public void setProductId(Long productId) { put("productId", productId); }
public Integer getQuantity() { return (Integer) get("quantity"); }
public void setQuantity(Integer quantity) { put("quantity", quantity); }
public Long getCouponId() { return (Long) get("couponId"); }
public void setCouponId(Long couponId) { put("couponId", couponId); }
public BigDecimal getTotalAmount() { return (BigDecimal) get("totalAmount"); }
public void setTotalAmount(BigDecimal amount) { put("totalAmount", amount); }
public boolean isValid() { return Boolean.TRUE.equals(get("valid")); }
public void setValid(boolean valid) { put("valid", valid); }
public String getErrorCode() { return (String) get("errorCode"); }
public void setErrorCode(String errorCode) { put("errorCode", errorCode); }
public String getErrorMsg() { return (String) get("errorMsg"); }
public void setErrorMsg(String errorMsg) { put("errorMsg", errorMsg); }
}
定义校验命令:
java
// 用户状态校验命令
public class UserValidateCommand implements Command {
private final UserService userService;
public UserValidateCommand(UserService userService) {
this.userService = userService;
}
@Override
public boolean execute(Context context) throws Exception {
OrderContext ctx = (OrderContext) context;
User user = userService.getById(ctx.getUserId());
if (user == null || user.getStatus() != UserStatus.NORMAL) {
ctx.setValid(false);
ctx.setErrorCode("USER_INVALID");
ctx.setErrorMsg("用户状态异常");
return false; // 终止链条
}
return true; // 继续执行
}
}
// 商品库存校验命令
public class StockValidateCommand implements Command {
private final ProductService productService;
public StockValidateCommand(ProductService productService) {
this.productService = productService;
}
@Override
public boolean execute(Context context) throws Exception {
OrderContext ctx = (OrderContext) context;
Product product = productService.getById(ctx.getProductId());
if (product == null || product.getStock() < ctx.getQuantity()) {
ctx.setValid(false);
ctx.setErrorCode("STOCK_INSUFFICIENT");
ctx.setErrorMsg("库存不足");
return false; // 终止链条
}
return true; // 继续执行
}
}
// 其他校验命令(优惠券、金额、风控)省略...
配置订单校验链:
java
@Service
public class OrderValidatorChain {
private final ChainBase validatorChain;
public OrderValidatorChain(UserService userService,
ProductService productService,
CouponService couponService,
RiskControlService riskControlService) {
ChainBase chain = new ChainBase();
// 按顺序添加校验命令
chain.addCommand(new UserValidateCommand(userService));
chain.addCommand(new StockValidateCommand(productService));
chain.addCommand(new CouponValidateCommand(couponService));
chain.addCommand(new AmountValidateCommand());
chain.addCommand(new RiskControlValidateCommand(riskControlService));
this.validatorChain = chain;
}
public ValidationResult validate(OrderRequest request) {
try {
OrderContext context = new OrderContext();
context.setUserId(request.getUserId());
context.setProductId(request.getProductId());
context.setQuantity(request.getQuantity());
context.setCouponId(request.getCouponId());
context.setTotalAmount(request.getTotalAmount());
context.setValid(true); // 默认为有效
validatorChain.execute(context);
if (context.isValid()) {
return ValidationResult.success();
} else {
return ValidationResult.fail(context.getErrorCode(), context.getErrorMsg());
}
} catch (Exception e) {
return ValidationResult.fail("SYSTEM_ERROR", "系统异常");
}
}
}
六、Apache Commons Chain核心优势
通过前面的实战案例,我们可以总结出Apache Commons Chain的核心优势:
6.1 标准化
- 成熟的开源框架:Apache Commons Chain是Apache软件基金会下的成熟项目
- 经过大量项目验证:在众多企业级项目中得到验证
- 社区支持:拥有活跃的社区支持和完善的文档
6.2 灵活性
- 支持多种配置方式:可以通过XML配置或编程方式配置责任链
- 动态调整:可以在运行时动态添加或删除Command
- 可嵌套:支持Chain的嵌套,可以构建复杂的处理流程
6.3 易用性
- 简单的API:只需要实现Command接口的execute方法
- 统一的Context:Context在整个链条中传递数据
- 清晰的执行流程:return true继续执行,return false终止链条
6.4 适用场景
特别适合以下场景:
- 复杂业务流程编排:订单处理、审批流程、工作流等
- 规则引擎:业务规则链式执行、风控规则等
- 任务处理:异步任务处理、批量任务处理等
- 审批系统:多级审批、条件分支审批等
6.5 与手写责任链的对比
| 对比项 | 手写责任链 | Apache Commons Chain |
|---|---|---|
| 实现复杂度 | 需要自己实现链式逻辑 | 使用现成的框架 |
| 灵活性 | 完全自定义 | 支持自定义但遵循框架规范 |
| 配置方式 | 硬编码 | 支持XML和编程配置 |
| 学习成本 | 低 | 需要学习框架API |
| 可维护性 | 取决于实现质量 | 统一标准,易于维护 |
| 生态支持 | 无 | Apache社区支持 |
建议:
- 对于简单场景:手写责任链即可
- 对于复杂场景:推荐使用Apache Commons Chain
- 对于企业级项目:推荐使用Apache Commons Chain
七、责任链模式的应用场景

典型应用场景:
- 审批流程:请假、报销、采购等企业审批
- 日志处理:不同级别日志输出到不同目标
- 异常处理:Web框架的异常处理链
- 过滤器链:请求预处理、编码转换、权限校验
7.1 适用条件
适合使用责任链模式的场景:
- 有多个对象可以处理请求,但不知道具体哪个对象处理
- 处理请求的对象需要动态指定
- 处理顺序很重要
- 需要在不影响客户端的情况下动态增删处理器
不适合使用责任链模式的场景:
- 只有一个处理器
- 处理顺序不重要
- 性能要求极高(责任链有性能开销)
八、最佳实践
8.1 性能优化
纯责任链 vs 不纯责任链
typescript
// 纯责任链:一个请求只能被一个处理器处理
protected boolean doHandle(Request request) {
if (canHandle(request)) {
// 处理并结束
return true;
}
return false; // 传递给下一个
}
// 不纯责任链:请求可以被多个处理器处理
protected void handle(Request request) {
if (canHandle(request)) {
doHandle(request); // 处理后继续传递
}
if (next != null) {
next.handle(request);
}
}
性能考虑:
- 控制链条长度:过长的链条会影响性能
- 避免循环引用:确保链条有终点
- 使用缓存:对处理器实例进行缓存
- 异步处理:对于耗时操作考虑异步处理
8.2 实战技巧
技巧1:使用建造者模式构建责任链
csharp
public class ResponsibilityChainBuilder {
private List<Handler> handlers = new ArrayList<>();
public ResponsibilityChainBuilder addHandler(Handler handler) {
handlers.add(handler);
return this;
}
public Handler build() {
if (handlers.isEmpty()) {
throw new IllegalStateException("至少需要一个处理器");
}
for (int i = 0; i < handlers.size() - 1; i++) {
handlers.get(i).setNext(handlers.get(i + 1));
}
return handlers.get(0);
}
}
// 使用
Handler chain = new ResponsibilityChainBuilder()
.addHandler(new TeamLeaderHandler("张三"))
.addHandler(new ManagerHandler("李四"))
.addHandler(new DirectorHandler("王五"))
.build();
技巧2:责任链+策略模式
java
// 策略接口
public interface HandleStrategy {
boolean canHandle(Request request);
boolean doHandle(Request request);
}
// 处理器使用策略
public class StrategyHandler extends AbstractHandler {
private HandleStrategy strategy;
public StrategyHandler(String name, HandleStrategy strategy) {
super(name);
this.strategy = strategy;
}
@Override
protected boolean canHandle(Request request) {
return strategy.canHandle(request);
}
@Override
protected boolean doHandle(Request request) {
return strategy.doHandle(request);
}
}
九、总结
核心要点
- 解耦:将请求者与处理者解耦,提高系统灵活性
- 动态:可以动态地增加或删除处理器
- 扩展:符合开闭原则,易于扩展新功能
- 灵活:可以自定义处理顺序和处理逻辑
责任链模式是一种简单但强大的设计模式,掌握它能让你的代码更加优雅、灵活。希望通过本文的实战案例,你能在实际项目中灵活运用这一模式。