Spring Shell 项目近日同时推送了两个维护版本——4.0.3 和 3.4.3,分别对应 Spring Shell 4.x(基于 Spring Boot 3.x)和 3.x(兼容 Spring Boot 2.x)两条线。对于已经在用 Spring Shell 的项目,这意味着常规的缺陷修复与依赖对齐;而对于还没接触过它的人,这恰好是个好时机:写一个交互式 CLI 工具,远比你想的简单。
Spring Shell 解决什么问题
Java 生态里做命令行工具,传统路径是 args4j、picocli、JCommander 这类解析库——它们能处理参数,但不会帮你做交互循环、命令注册、补全提示、输出格式化这些"壳"的事情。Spring Shell 把这些全包了:
- 自动发现
@ShellComponent并注册为可用命令 - 内置 REPL 循环,用户敲完一条命令后继续等待下一条
- TAB 补全、命令历史、彩色输出
- 与 Spring 生态无缝整合(依赖注入、配置绑定、条件装配)
本质上,它让你用写 REST Controller 的方式写 CLI 命令。
快速起步:一个能跑的项目
下面用 Spring Shell 4.x(Spring Boot 3.x)搭一个最小可运行项目。如果你还在 Spring Boot 2.x,把依赖版本换成 3.4.3 即可,代码本身无差别。
项目依赖
Maven pom.xml 关键部分:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.1</version>
</parent>
<dependencies>
<!-- Spring Shell 核心 -->
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell-starter</artifactId>
<version>4.0.3</version>
</dependency>
</dependencies>
Gradle 用户对应写法:
dependencies {
implementation 'org.springframework.shell:spring-shell-starter:4.0.3'
}
注意:
spring-shell-starter已经包含了 Spring Boot 自动配置和内置的 REPL 引擎,不需要额外加spring-boot-starter。
写两个命令
package com.example.cli;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
@ShellComponent
public class GreetCommands {
@ShellMethod(value = "向某人打招呼", key = "hello")
public String hello(
@ShellOption(defaultValue = "World") String name,
@ShellOption(help = "重复次数", defaultValue = "1") int count) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append("Hello, ").append(name).append("!\n");
}
return sb.toString();
}
@ShellMethod(value = "计算两数之和", key = "add")
public int add(int a, int b) {
return a + b;
}
}
启动类
package com.example.cli;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CliApplication {
public static void main(String[] args) {
SpringApplication.run(CliApplication.class, args);
}
}
运行
mvn spring-boot:run
启动后你会进入交互式 shell:
shell:>hello --name Alice --count 3
Hello, Alice!
Hello, Alice!
Hello, Alice!
shell:>add 7 15
22
shell:>help
AVAILABLE COMMANDS
add: 计算两数之和
hello: 向某人打招呼
help: 显示帮助
...
TAB 补全也直接可用——输入 he 再按 TAB,自动补全为 hello;输入 --n 按 TAB,补全为 --name。
进阶:让命令真正有用
上面是最小骨架。真实项目里,你通常需要注入 Service、读取配置、处理异常。Spring Shell 的命令类就是普通的 Spring Bean,一切注入规则照旧:
@ShellComponent
public class DbCommands {
private final UserRepository userRepo;
public DbCommands(UserRepository userRepo) {
this.userRepo = userRepo;
}
@ShellMethod(value = "按邮箱查找用户", key = "find-user")
public String findByEmail(@ShellOption String email) {
return userRepo.findByEmail(email)
.map(u -> u.getName() + " | " + u.getRole())
.orElse("未找到: " + email);
}
@ShellMethod(value = "列出所有用户", key = "list-users")
public String listUsers() {
List<User> users = userRepo.findAll();
if (users.isEmpty()) {
return "暂无用户数据";
}
return users.stream()
.map(u -> String.format("%-20s %-10s %s", u.getName(), u.getRole(), u.getEmail()))
.collect(Collectors.joining("\n"));
}
}
不需要额外配置——只要 UserRepository 是 Spring Bean,自动注入就生效。
3.x 与 4.x 的选择
这次双线发布,核心区别在于底层 Spring Boot 版本:
| 维度 | Spring Shell 3.4.3 | Spring Shell 4.0.3 |
|---|---|---|
| Spring Boot | 2.x | 3.x |
| Java 最低版本 | 8+ | 17+ |
| Jakarta / Javax | javax | jakarta |
| 长期维护 | 收窄,仅修复 | 主线,持续演进 |
选择建议:
- 新项目直接用 4.0.3 + Spring Boot 3.x,没有理由走旧线。
- 已有 Spring Boot 2.x 项目:如果只是内部运维工具、短期不改,留在 3.4.3 即可;如果计划整体迁移到 Boot 3.x,顺手升到 4.0.3。
- 两条线的命令编写方式完全一致,迁移成本几乎为零——换依赖版本、改
javax为jakarta(如果用了),其余代码不动。
上手检查清单
把 Spring Shell 引入项目前,过一遍这几项:
- Java 版本对齐——4.x 要求 17+,3.x 要求 8+,和你的 Boot 版本一致。
- 排除 Web 依赖——CLI 项目通常不需要
spring-boot-starter-web,同时引入会导致端口冲突或启动两个上下文。如果确实需要两者共存,用@Profile或条件装配隔离。 - 非交互模式——加
--参数可以直接执行单条命令后退出,适合 CI 脚本:java -jar app.jar -- add 3 5。 - 自定义退出命令——默认用
exit,可以通过ShellCustomizer改成quit或其他关键词。 - 输出美化——返回
String是最简单的;需要表格、进度条等富输出,可用Terminal对象直接操作 ANSI 序列。
Spring Shell 把"写一个命令行工具"的门槛压到了和"写一个 REST 接口"同一水平。4.0.3 和 3.4.3 的发布本身是常规维护,但它们提醒你:这条线在持续走,你的运维脚本、数据诊断工具、内部 CLI,值得用更规范的方式来做。