1. 简介
Apache Commons Chain 是一个基于责任链模式的库,主要用于组织复杂的处理流程,其中多个接收者可以依次处理同一请求。本文通过一个ATM取款的实战案例,带你快速掌握这个工具的核心用法。
2. Maven 依赖
首先通过 Maven 导入最新版本(当前为 1.2):
<dependency>
<groupId>commons-chain</groupId>
<artifactId>commons-chain</artifactId>
<version>1.2</version>
</dependency>
最新版本可查看 Maven 中央仓库
3. 示例链设计
ATM 系统接收取款金额后,会依次传递给多个处理器:
- ✅ 计算各种面额钞票的分配数量
- ✅ 向银行发送交易通知
- ✅ 向客户发送交易通知
典型责任链的应用场景,简单粗暴但高效。
4. 链上下文(Chain Context)
上下文存储了交易过程中的所有状态数据。ATM 取款需要管理这些关键信息:
- 总取款金额
- 100元面额数量
- 50元面额数量
- 10元面额数量
- 剩余待分配金额
通过 AtmRequestContext
类实现:
public class AtmRequestContext extends ContextBase {
int totalAmountToBeWithdrawn;
int noOfHundredsDispensed;
int noOfFiftiesDispensed;
int noOfTensDispensed;
int amountLeftToBeWithdrawn;
// 标准 setter & getter
}
5. 命令(Command)
命令接收上下文并执行具体处理逻辑。我们为每个处理步骤创建独立命令:
public class HundredDenominationDispenser implements Command {
@Override
public boolean execute(Context context) throws Exception {
int amountLeftToBeWithdrawn = (int) context.get("amountLeftToBeWithdrawn");
if (amountLeftToBeWithdrawn >= 100) {
context.put("noOfHundredsDispensed", amountLeftToBeWithdrawn / 100);
context.put("amountLeftToBeWithdrawn", amountLeftToBeWithdrawn % 100);
}
return false;
}
}
FiftyDenominationDispenser
和TenDenominationDispenser
的实现逻辑类似,这里省略重复代码
6. 链(Chain)
链是按顺序执行的命令集合。我们的取款链包含三个面额分配命令和一个审计过滤器:
public class AtmWithdrawalChain extends ChainBase {
public AtmWithdrawalChain() {
super();
addCommand(new HundredDenominationDispenser());
addCommand(new FiftyDenominationDispenser());
addCommand(new TenDenominationDispenser());
addCommand(new AuditFilter());
}
}
⚠️ 注意:当链中任意命令返回 true
时,会强制终止整个链的执行
7. 过滤器(Filter)
过滤器是特殊的命令,额外提供 postProcess
方法在链执行完成后调用。我们的审计过滤器负责发送通知:
public class AuditFilter implements Filter {
@Override
public boolean postprocess(Context context, Exception exception) {
// 向银行和用户发送通知
return false;
}
@Override
public boolean execute(Context context) throws Exception {
return false;
}
}
8. 链目录(Chain Catalog)
目录管理所有链和命令的逻辑名称映射。我们的目录注册了取款链:
public class AtmCatalog extends CatalogBase {
public AtmCatalog() {
super();
addCommand("atmWithdrawalChain", new AtmWithdrawalChain());
}
}
9. 使用链
通过测试用例演示完整处理流程:
public class AtmChainTest {
@Test
public void givenInputsToContext_whenAppliedChain_thenExpectedContext() throws Exception {
Context context = new AtmRequestContext();
context.put("totalAmountToBeWithdrawn", 460);
context.put("amountLeftToBeWithdrawn", 460);
Catalog catalog = new AtmCatalog();
Command atmWithdrawalChain = catalog.getCommand("atmWithdrawalChain");
atmWithdrawalChain.execute(context);
assertEquals(460, (int) context.get("totalAmountToBeWithdrawn"));
assertEquals(0, (int) context.get("amountLeftToBeWithdrawn"));
assertEquals(4, (int) context.get("noOfHundredsDispensed"));
assertEquals(1, (int) context.get("noOfFiftiesDispensed"));
assertEquals(1, (int) context.get("noOfTensDispensed"));
}
}
10. 总结
本文通过 ATM 取款案例展示了 Apache Commons Chain 的核心用法:
- ✅ 通过上下文管理状态流转
- ✅ 命令封装独立处理逻辑
- ✅ 链实现流程编排
- ✅ 过滤器增强后处理能力
深入学习可查阅官方文档:Apache Commons Chain Cookbook
完整代码见:GitHub 示例