基于SpringBoot打造一个通用CLI命令系统

 更新时间:2025年12月15日 08:17:14   作者:风象南  
在日常开发中,某些情况下可能需要为服务提供一个命令行工具(CLI),方便运维、调试或者远程调用业务接口,下面我们就来使用SpringBoot打造一个通用的CLI命令系统吧

一、背景

在日常开发中,某些情况下可能需要为服务提供一个命令行工具(CLI),方便运维、调试或者远程调用业务接口。

假设我们有一个 Spring Boot 服务,提供多个接口,例如:

  • 获取用户列表:/users?type=admin
  • 获取角色列表:/roles?level=manager
  • 获取系统状态:/system/status
  • 批量导入数据:/data/import
  • 生成报表:/report/generate

传统做法:

  • 每个接口在 CLI 客户端写一条命令
  • CLI 方法直接调用服务端 REST 接口
  • 硬编码的服务地址和参数格式

问题

  • 接口多时,CLI 方法数量激增,代码冗余严重
  • 每次新增接口都需要修改客户端,发布新版本
  • 维护成本高,不同环境的配置分散
  • 缺乏统一的认证、授权和日志机制
  • 开发效率低下,重复劳动多

解决方案通用命令 + 动态分发

  • CLI 只维护一条通用命令 exec
  • 根据参数动态路由到服务端对应的 Service Bean
  • 服务端统一管理,支持动态扩展
  • 一次开发,多处复用

二、方案设计

1. 核心架构

我们设计了一套基于 Spring Boot + Spring Shell 的通用CLI系统,采用分层架构设计:

客户端(Spring Shell)  <--HTTP-->  服务端(Spring Boot)
       |                              |
    通用命令exec                    统一控制器(/cli)
       |                              |
    动态参数                      动态Bean分发
       |                              |
  单一入口命令                  多个CommandHandler
       |                              |
    REST通信                    业务逻辑处理

设计原则

单一职责:客户端只负责命令解析和HTTP通信,服务端只负责业务逻辑 开闭原则:对扩展开放(新增服务),对修改关闭(不需改客户端) 依赖倒置:依赖抽象的CommandHandler接口,而非具体实现 最小知识:客户端无需知道服务端的具体实现细节

2. 客户端设计

在 CLI 客户端定义一条通用命令 exec

@ShellComponent
public class ExecCommand {

    @ShellMethod(key = "exec", value = "执行远程服务命令")
    public String executeCommand(
            @ShellOption(value = {"", "service"}, help = "服务名称") String serviceName,
            @ShellOption(value = "--args", help = "命令参数", arity = 100) String[] args) {

        // 构建请求并发送到服务端
        CommandRequest request = new CommandRequest(serviceName, Arrays.asList(args));
        return httpClient.post("/cli", request);
    }
}

使用示例

> exec userService --args list
user1, user2, user3

> exec roleService --args users admin
role1, role2

> exec systemService --args status
系统正常运行

3. 服务端设计

服务端提供统一接口 /cli,根据服务名动态分发:

@RestController
@RequestMapping("/cli")
public class CliController {

    @Autowired
    private ApplicationContext applicationContext;

    @PostMapping
    public String execute(@RequestBody CommandRequest request) {
        String serviceName = request.getService();
        String[] args = request.getArgs().toArray(new String[0]);

        // 动态获取 Service Bean
        Object serviceBean = applicationContext.getBean(serviceName);

        // 执行命令
        if (serviceBean instanceof CommandHandler handler) {
            return handler.handle(args);
        }

        return "服务未找到";
    }
}

4. 统一接口规范

所有需要通过CLI调用的服务都必须实现 CommandHandler 接口:

public interface CommandHandler {
    String handle(String[] args);
    default String getDescription() { return "命令描述"; }
    default String getUsage() { return "使用说明"; }
}

示例服务实现:

@Service("userService")
public class UserService implements CommandHandler {

    @Override
    public String handle(String[] args) {
        if (args.length == 0) return getUsage();

        switch (args[0]) {
            case "list":
                return listUsers(args.length > 1 ? args[1] : null);
            case "get":
                return getUser(args[1]);
            default:
                return "未知命令: " + args[0];
        }
    }

    private String listUsers(String type) {
        // 实现获取用户列表逻辑
        return "用户列表...";
    }
}

三、方案优势

1. 客户端统一命令

  • Shell 只需维护一条 exec 命令
  • 新增服务无需修改客户端代码

2. 服务端动态分发

  • 新增接口无需修改 CLI
  • 统一接口入口便于权限控制与日志审计

3. 易扩展

  • 支持任意参数数量、类型
  • 可结合 OpenAPI 自动生成命令提示与帮助信息

4. 逻辑解耦

  • CLI 仅做命令解析和 HTTP 调用
  • 业务逻辑完全在服务端

四、安全控制

1. 服务白名单

通过配置文件限制可访问的服务:

cli:
  allowed-services:
    - userService
    - roleService
    - systemService

2. 参数验证

使用 Spring Validation 进行请求参数校验,防止恶意输入。

3. 访问日志

记录所有CLI调用,便于审计和问题追踪:

logger.info("CLI请求 - 服务: {}, 参数: {}, 来源: {}",
    serviceName, Arrays.toString(args), httpRequest.getRemoteAddr());

五、实际应用场景

1. 运维场景

# 查看系统状态
exec systemService --args status

# 重启服务
exec serviceManager --args restart userService

# 查看日志
exec logService --args tail 100 error

2. 调试场景

# 查看用户详情
exec userService --args get 123

# 测试接口
exec testService --args simulate /api/orders

# 清理缓存
exec cacheService --args clear all

3. 批量操作

# 批量导入用户
exec userService --args import users.csv

# 批量更新角色
exec roleService --args batchUpdate role-mapping.json

六、扩展功能

1. 交互增强

  • Tab 补全:自动补全服务名和参数
  • 命令历史:保存执行历史,支持上下键浏览
  • 颜色输出:不同类型信息使用不同颜色显示

2. 结果格式化

private String formatResponse(String data) {
    try {
        Object json = objectMapper.readValue(data, Object.class);
        return objectMapper.writerWithDefaultPrettyPrinter()
                          .writeValueAsString(json);
    } catch (Exception e) {
        return data;
    }
}

3. 脚本模式

支持从文件执行命令序列:

exec script --args commands.txt

七、总结

本文介绍的"通用命令+动态分发"方案,通过Spring Boot + Spring Shell构建,使用单一 exec 命令实现多服务动态调用,大幅简化了CLI系统的维护复杂度。

到此这篇关于基于SpringBoot打造一个通用CLI命令系统的文章就介绍到这了,更多相关SpringBoot CLI命令系统内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java项目开发命名规范(动力节点Java学院整理)

    Java项目开发命名规范(动力节点Java学院整理)

    定义这个规范的目的是让项目中所有的文档都看起来像一个人写的,增加可读性,减少项目组中因为换人而带来的损失。下面给大家分享java开发命名规范,一起看看吧
    2017-03-03
  • JDK14性能管理工具之Jconsole的使用详解

    JDK14性能管理工具之Jconsole的使用详解

    JConsole是JDK自带的管理工具,在JAVA_HOME/bin下面,直接命令JConsole即可开启JConsole。接下来通过本文给大家分享JDK14性能管理工具之Jconsole的使用,感兴趣的朋友一起看看吧
    2020-05-05
  • 浅谈Java异常的Exception e中的egetMessage()和toString()方法的区别

    浅谈Java异常的Exception e中的egetMessage()和toString()方法的区别

    下面小编就为大家带来一篇浅谈Java异常的Exception e中的egetMessage()和toString()方法的区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • Mybatis配置之<properties>属性配置元素解析

    Mybatis配置之<properties>属性配置元素解析

    这篇文章主要介绍了Mybatis配置之<properties>属性配置元素解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Spring 注入static属性值方式

    Spring 注入static属性值方式

    文本介绍了Spring如何从属性文件给static属性注入值,在写一些与配置相关的工具类时常用。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java中Scanner使用方式:单行/多行输入

    Java中Scanner使用方式:单行/多行输入

    这篇文章主要介绍了Java中Scanner使用方式:单行/多行输入,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • 基于@RequestBody注解只能注入对象和map的解决

    基于@RequestBody注解只能注入对象和map的解决

    这篇文章主要介绍了@RequestBody注解只能注入对象和map的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • SpringBoot+Vue添加腾讯云人脸识别的项目实践

    SpringBoot+Vue添加腾讯云人脸识别的项目实践

    人脸识别是一种基于人脸特征进行身份认证和识别的技术,本文主要介绍了SpringBoot+Vue添加腾讯云人脸识别的项目实践,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08
  • Java代码重用之功能与上下文重用

    Java代码重用之功能与上下文重用

    代码重用通常使得程序开发更加快速,并使得 BUG 减少。一旦一段代码被封装和重用,那么只需要检查很少的一段代码即可确保程序的正确性。接下来通过本文给大家介绍Java代码重用之功能与上下文重用的相关知识,感兴趣的朋友跟随脚本之家小编一起学习吧
    2018-05-05
  • Spring的@CrossOrigin注解使用与CrossFilter对象自定义详解

    Spring的@CrossOrigin注解使用与CrossFilter对象自定义详解

    这篇文章主要介绍了Spring的@CrossOrigin注解使用与CrossFilter对象自定义详解,跨域,指的是浏览器不能执行其他网站的脚本,它是由浏览器的同源策略造成的,是浏览器施加的安全限制,所谓同源是指,域名,协议,端口均相同,需要的朋友可以参考下
    2023-12-12

最新评论