1. 概述

简单来说,Spring Shell项目基于Spring编程模型,提供了交互式命令行处理能力,可以快速构建功能完整的CLI工具。

本文将深入探讨其核心特性、关键类和注解,并通过实际案例实现自定义命令和个性化配置。

2. Maven依赖

首先在pom.xml中添加spring-shell依赖:

<dependency>
    <groupId>org.springframework.shell</groupId>
    <artifactId>spring-shell</artifactId>
    <version>1.2.0.RELEASE</version>
</dependency>

最新版本可在Maven中央仓库获取。

3. 访问Shell

在应用中访问Shell主要有两种方式:

方式一:直接启动交互式Shell

在应用入口启动Shell,让用户直接输入命令:

public static void main(String[] args) throws IOException {
    Bootstrap.main(args);
}

方式二:编程式调用

获取JLineShellComponent实例,通过代码执行命令:

Bootstrap bootstrap = new Bootstrap();
JLineShellComponent shell = bootstrap.getJLineShellComponent();
shell.executeCommand("help");

⚠️ 本文采用第一种方式演示,但源码中提供了第二种方式的测试用例。

4. 命令开发

Shell内置了clearhelpexit等基础命令。自定义命令需满足:

  • 实现CommandMarker接口的Spring组件
  • 方法使用@CliCommand注解
  • 所有参数必须使用@CliOption注解(否则会报错)

4.1 添加自定义命令

步骤1:注册命令组件

META-INF/spring/spring-shell-plugin.xml中配置组件扫描:

<beans ... >
    <context:component-scan base-package="com.example.shell" />
</beans>

步骤2:实现命令方法

创建两个命令:获取网页内容和保存网页内容:

@Component
public class WebCommands implements CommandMarker {

    @CliCommand(value = { "web-get", "wg" })
    public String webGet(
      @CliOption(key = "url") String url) {
        return fetchUrlContent(url);
    }
    
    @CliCommand(value = { "web-save", "ws" })
    public String webSave(
      @CliOption(key = "url") String url,
      @CliOption(key = { "out", "file" }) String file) {
        String content = fetchUrlContent(url);
        try (PrintWriter out = new PrintWriter(file)) {
            out.write(content);
        }
        return "保存完成";
    }
}

✅ 技巧:valuekey支持多别名,如web-getwg等效

验证命令效果:

spring-shell>web-get --url https://www.google.com
<!doctype html ... 
spring-shell>web-save --url https://www.google.com --out contents.txt
保存完成

4.2 命令动态控制

使用@CliAvailabilityIndicator注解实现命令的动态启用/禁用:

private boolean adminEnabled = false;

@CliAvailabilityIndicator(value = "web-save")
public boolean isWebSaveAvailable() {
    return adminEnabled;
}

@CliCommand(value = "admin-enable")
public String enableAdmin() {
    adminEnabled = true;
    return "管理员命令已启用";
}

验证动态控制:

spring-shell>web-save --url https://www.google.com --out contents.txt
命令 'web-save' 存在但当前不可用
(输入 'help' 查看帮助)
spring-shell>admin-enable
管理员命令已启用
spring-shell>web-save --url https://www.google.com --out contents.txt
保存完成

4.3 必填参数

通过mandatory属性设置必填参数:

@CliOption(key = { "out", "file" }, mandatory = true)

验证必填效果:

spring-shell>web-save --url https://www.google.com
必须指定参数 (--out)

4.4 默认参数

空字符串key表示默认参数,接收未命名的输入值:

@CliOption(key = { "", "url" })

验证默认参数:

spring-shell>web-get https://www.google.com
<!doctype html ...

4.5 帮助信息

通过help属性添加命令和参数说明:

@CliCommand(
  value = { "web-get", "wg" },
  help = "获取并显示URL内容")
public String webGet(
  @CliOption(
    key = "url",
    help = "要获取内容的URL地址"
  ) String url) {
    // ...
}

查看帮助效果:

spring-shell>help web-get
关键字:                    web-get
关键字:                    wg
描述:                      获取并显示URL内容
  关键字:                  ** default **
  关键字:                  url
    帮助:                   要获取内容的URL地址
    必填:                  false
    默认值:                '__NULL__'

* web-get - 获取并显示URL内容
* wg - 获取并显示URL内容

5. 个性化配置

通过实现以下接口可深度定制Shell:

  • BannerProvider:启动横幅
  • PromptProvider:命令提示符
  • HistoryFileNameProvider:历史记录文件名

⚠️ 需使用@Order注解覆盖默认实现

5.1 自定义启动横幅

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomBanner extends DefaultBannerProvider {

    public String getBanner() {
        return new StringBuilder()
            .append("=======================================\n")
            .append("*          Baeldung Shell             *\n")
            .append("=======================================\n")
            .append("版本: ").append(getVersion())
            .toString();
    }

    public String getVersion() {
        return "1.0.1";
    }

    public String getWelcomeMessage() {
        return "欢迎使用Baeldung命令行工具";
    }

    public String getProviderName() {
        return "Baeldung横幅提供者";
    }
}

5.2 自定义提示符

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomPrompt extends DefaultPromptProvider {

    public String getPrompt() {
        return "baeldung-shell";
    }

    public String getProviderName() {
        return "Baeldung提示符提供者";
    }
}

5.3 自定义历史记录文件

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomHistoryFile extends DefaultHistoryFileNameProvider {

    public String getHistoryFileName() {
        return "baeldung-shell.log";
    }

    public String getProviderName() {
        return "Baeldung历史记录提供者";
    }
}

最终启动效果:

=======================================
*          Baeldung Shell             *
=======================================
版本:1.0.1
欢迎使用Baeldung命令行工具
baeldung-shell>

6. 类型转换器

Spring Shell已内置常见类型转换器(Integer/Date/Enum/File等)。通过实现Converter接口可扩展自定义类型转换:

6.1 URL类型转换器

@Component
public class URLConverter implements Converter<URL> {

    public URL convertFromText(
      String value, Class<?> targetType, String context) {
        return new URL(value);
    }

    public boolean getAllPossibleValues(
      List<Completion> completions,
      Class<?> targetType,
      String input,
      String context,
      MethodTarget target) {
        return false;
    }

    public boolean supports(Class<?> targetType, String context) {
        return URL.class.isAssignableFrom(targetType);
    }
}

6.2 使用自定义类型

修改命令方法参数类型:

public String webSave(... URL url) {
    // 直接使用URL对象
}

✅ 转换器会自动处理String到URL的转换,命令使用方式不变

7. 总结

本文系统介绍了Spring Shell的核心能力:

  • ✅ 通过注解快速开发命令
  • ✅ 动态控制命令可用性
  • ✅ 个性化配置Shell外观
  • ✅ 扩展类型转换系统

这些特性使构建企业级命令行工具变得简单高效。对于需要快速开发CLI工具的场景,Spring Shell是个不错的选择。


原始标题:A CLI with Spring Shell | Baeldung