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;
    }
}

FiftyDenominationDispenserTenDenominationDispenser 的实现逻辑类似,这里省略重复代码

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 示例


原始标题:Apache Commons Chain