1. 简介

本文将介绍 Airline —— 一个基于注解的 Java 库,用于快速构建命令行接口(CLI)。

如果你厌倦了手动解析 args[],还要写一堆 if-else 来处理参数,那 Airline 绝对能让你眼前一亮。它通过注解驱动的方式,把 CLI 的定义变得声明式、类型安全,而且代码干净得不像 Java(✅ 真的,不夸张)。

2. 使用场景

开发命令行工具时,我们通常希望用户能通过参数自定义行为,比如设置日志级别、数据库连接、开关功能等。Git CLI 就是个经典例子:简洁、直观、强大。

但 Java 原生处理命令行参数的方式太原始了,写起来啰嗦还容易出错。虽然有 Apache Commons CLI 这类工具,但配置依然繁琐。Airline 的出现就是为了解决这个问题:

用注解代替样板代码
自动支持 help、参数校验、互斥选项等常见需求
支持嵌套命令、子命令、复杂参数约束

我们接下来会实现一个模拟配置管理的 CLI 工具,支持:

  • 设置日志级别(setup-log
  • 配置数据库连接(setup-db
  • 自动 help 提示

并通过这个例子,看看 Airline 能不能扛住真实场景的复杂度。

3. 项目依赖

先加 Maven 依赖:

<dependency>
    <groupId>com.github.rvesse</groupId>
    <artifactId>airline</artifactId>
    <version>2.7.2</version>
</dependency>

搞定,就这一个依赖,无其他隐式依赖,轻量。

4. 快速搭建 CLI 入口

先定义主入口类 CommandLine

@Cli(name = "baeldung-cli",
  description = "Baeldung Airline Tutorial",
  defaultCommand = Help.class)
public class CommandLine {
    public static void main(String[] args) {
        Cli<Runnable> cli = new Cli<>(CommandLine.class);
        Runnable cmd = cli.parse(args);
        cmd.run();
    }
}

关键点:

  • @Cli 注解定义 CLI 名称、描述和默认命令
  • defaultCommand = Help.class:不输入任何参数时,默认显示 help
  • Help 是 Airline 内置的 help 命令,支持 -h--help

就这么几行,一个带 help 的 CLI 框架就搭好了 ❗

5. 实现第一个命令:日志配置

我们来写一个 setup-log 命令,用于控制日志是否开启 verbose 模式。

@Command(name = "setup-log", description = "Setup our log")
public class LoggingCommand implements Runnable {

    @Inject
    private HelpOption<LoggingCommand> help;
    
    @Option(name = { "-v", "--verbose" }, 
      description = "Set log verbosity on/off")
    private boolean verbose = false;

    @Override
    public void run() {
        if (!help.showHelpIfRequested())
            System.out.println("Verbosity: " + verbose);
    }
}

关键细节解析

  • @Command:声明这是一个 CLI 命令,用户输入 setup-log 时触发
  • @Inject HelpOption:注入 help 功能,调用 showHelpIfRequested() 可自动处理 --help
  • @Option:定义命令行选项,支持短名 -v 和长名 --verbose
  • 实现 Runnable 接口,run() 方法里写业务逻辑

⚠️ 注意:help.showHelpIfRequested() 一定要先调用,否则 help 不会生效。

注册命令

别忘了把新命令注册到 @Cli 注解里:

@Cli(name = "baeldung-cli",
     description = "Baeldung Airline Tutorial",
     defaultCommand = Help.class,
     commands = { LoggingCommand.class, Help.class })
public class CommandLine {
    // main 方法不变
}

现在可以测试了:

$ java -jar cli.jar setup-log -v
Verbosity: true
$ java -jar cli.jar setup-log --help
# 自动生成格式化的 help 文档

是不是简单粗暴?连 help 文档都自动生成了 ✅

6. 复杂参数处理:数据库配置

真实场景中,参数往往更复杂。比如配置数据库时,我们可能需要:

  • 指定数据库类型(仅限 mysql/postgresql/mongodb)
  • 支持 URL 模式 或 host+port 模式(互斥)
  • host 模式下必须提供用户名密码
  • 可指定多个驱动 jar 包路径

Airline 全都能搞定。

6.1 枚举式参数限制

限制数据库类型只能是三种之一:

@AllowedRawValues(allowedValues = { "mysql", "postgresql", "mongodb" })
@Option(type = OptionType.COMMAND,
  name = {"-d", "--database"},
  description = "Type of RDBMS.",
  title = "RDBMS type: mysql|postgresql|mongodb")
protected String rdbmsMode;

@AllowedRawValues 会自动校验输入值,非法值直接报错。

6.2 互斥参数(Mutually Exclusive)

URL 模式和 Host 模式不能同时出现:

@Option(type = OptionType.COMMAND,
  name = {"--rdbms:url", "--url"},
  description = "URL to use for connection to RDBMS.",
  title = "RDBMS URL")
@MutuallyExclusiveWith(tag="mode")
@Pattern(pattern="^(http://.*):(\\d*)(.*)u=(.*)&p=(.*)")
protected String rdbmsUrl = "";
    
@Option(type = OptionType.COMMAND,
  name = {"--rdbms:host", "--host"},
  description = "Host to use for connection to RDBMS.",
  title = "RDBMS host")
@MutuallyExclusiveWith(tag="mode")
protected String rdbmsHost = "";
  • @MutuallyExclusiveWith(tag="mode"):标记这两个选项互斥
  • @Pattern:正则校验 URL 格式(注意 Java 字符串转义)

6.3 条件必填(Required Only If)

只有使用 host 模式时,才要求用户名密码:

@RequiredOnlyIf(names={"--rdbms:host", "--host"})
@Option(type = OptionType.COMMAND,
  name = {"--rdbms:user", "-u", "--user"},
  description = "User for login to RDBMS.",
  title = "RDBMS user")
protected String rdbmsUser;

@RequiredOnlyIf(names={"--rdbms:host", "--host"})
@Option(type = OptionType.COMMAND,
  name = {"--rdbms:password", "--password"},
  description = "Password for login to RDBMS.",
  title = "RDBMS password")
protected String rdbmsPassword;

@RequiredOnlyIf 指定依赖的选项名,满足条件时才校验必填。

6.4 多值参数(List 支持)

支持传多个 jar 包路径:

@Option(type = OptionType.COMMAND,
  name = {"--driver", "--jars"},
  description = "List of drivers",
  title = "--driver <PATH_TO_YOUR_JAR> --driver <PATH_TO_YOUR_JAR>")
protected List<String> jars = new ArrayList<>();

用户可以这样用:

--driver /path/to/mysql.jar --driver /path/to/postgres.jar

Airline 会自动收集所有 --driver 的值放入 List

6.5 注册数据库命令

最后别忘了注册:

@Cli(
    name = "baeldung-cli",
    description = "Baeldung Airline Tutorial",
    defaultCommand = Help.class,
    commands = { LoggingCommand.class, DatabaseSetupCommand.class, Help.class }
)

7. 运行效果

默认 help 输出

$ baeldung-cli

usage: baeldung-cli <command> [ <args> ]

Commands are:
    help        Display help information
    setup-db    Setup our database
    setup-log   Setup our log

See 'baeldung-cli help <command>' for more information on a specific command.

命令级 help

$ baeldung-cli setup-log --help

NAME
        baeldung-cli setup-log - Setup our log

SYNOPSIS
        baeldung-cli setup-log [ {-h | --help} ] [ {-v | --verbose} ]

OPTIONS
        -h, --help
            Display help information

        -v, --verbose
            Set log verbosity on/off

实际调用示例

# 使用 URL 模式
$ baeldung-cli setup-db --database mysql --url "http://localhost:3306/db?u=admin&p=123"

# 使用 Host 模式(必须带用户密码)
$ baeldung-cli setup-db --database postgresql --host localhost --user admin --password secret

# 错误用法:混用互斥参数
$ baeldung-cli setup-db --url http://... --host localhost
# ❌ 报错:--url 和 --host 不能同时使用

8. 总结

Airline 真的是 Java CLI 开发的“提效神器”:

注解驱动,代码简洁:几乎不用写解析逻辑
功能完整:help、校验、互斥、依赖、多值全支持
类型安全:编译期检查命令结构
适合复杂 CLI:支持嵌套命令、全局选项、自定义解析器等高级特性

对于中大型命令行工具,尤其是需要发布给用户使用的,Airline 能大幅降低维护成本,避免各种参数解析的“坑”。

📌 踩坑提醒

  • 所有带 @Option 的字段必须是 非 private(至少 protected),否则反射访问不到
  • @RequiredOnlyIf 等约束注解依赖字段名匹配,注意大小写和别名一致性
  • 多模块项目中,确保命令类能被正确加载(避免类加载问题)

完整代码已上传至 GitHub:https://github.com/baeldung/tutorials/tree/master/libraries-cli


原始标题:Parsing Command-Line Parameters with Airline | Baeldung