基于SpringBoot打造一個(gè)通用CLI命令系統(tǒng)
一、背景
在日常開(kāi)發(fā)中,某些情況下可能需要為服務(wù)提供一個(gè)命令行工具(CLI),方便運(yùn)維、調(diào)試或者遠(yuǎn)程調(diào)用業(yè)務(wù)接口。
假設(shè)我們有一個(gè) Spring Boot 服務(wù),提供多個(gè)接口,例如:
- 獲取用戶(hù)列表:
/users?type=admin - 獲取角色列表:
/roles?level=manager - 獲取系統(tǒng)狀態(tài):
/system/status - 批量導(dǎo)入數(shù)據(jù):
/data/import - 生成報(bào)表:
/report/generate
傳統(tǒng)做法:
- 每個(gè)接口在 CLI 客戶(hù)端寫(xiě)一條命令
- CLI 方法直接調(diào)用服務(wù)端 REST 接口
- 硬編碼的服務(wù)地址和參數(shù)格式
問(wèn)題:
- 接口多時(shí),CLI 方法數(shù)量激增,代碼冗余嚴(yán)重
- 每次新增接口都需要修改客戶(hù)端,發(fā)布新版本
- 維護(hù)成本高,不同環(huán)境的配置分散
- 缺乏統(tǒng)一的認(rèn)證、授權(quán)和日志機(jī)制
- 開(kāi)發(fā)效率低下,重復(fù)勞動(dòng)多
解決方案:通用命令 + 動(dòng)態(tài)分發(fā)
- CLI 只維護(hù)一條通用命令
exec - 根據(jù)參數(shù)動(dòng)態(tài)路由到服務(wù)端對(duì)應(yīng)的 Service Bean
- 服務(wù)端統(tǒng)一管理,支持動(dòng)態(tài)擴(kuò)展
- 一次開(kāi)發(fā),多處復(fù)用
二、方案設(shè)計(jì)
1. 核心架構(gòu)
我們?cè)O(shè)計(jì)了一套基于 Spring Boot + Spring Shell 的通用CLI系統(tǒng),采用分層架構(gòu)設(shè)計(jì):
客戶(hù)端(Spring Shell) <--HTTP--> 服務(wù)端(Spring Boot)
| |
通用命令exec 統(tǒng)一控制器(/cli)
| |
動(dòng)態(tài)參數(shù) 動(dòng)態(tài)Bean分發(fā)
| |
單一入口命令 多個(gè)CommandHandler
| |
REST通信 業(yè)務(wù)邏輯處理
設(shè)計(jì)原則
單一職責(zé):客戶(hù)端只負(fù)責(zé)命令解析和HTTP通信,服務(wù)端只負(fù)責(zé)業(yè)務(wù)邏輯 開(kāi)閉原則:對(duì)擴(kuò)展開(kāi)放(新增服務(wù)),對(duì)修改關(guān)閉(不需改客戶(hù)端) 依賴(lài)倒置:依賴(lài)抽象的CommandHandler接口,而非具體實(shí)現(xiàn) 最小知識(shí):客戶(hù)端無(wú)需知道服務(wù)端的具體實(shí)現(xiàn)細(xì)節(jié)
2. 客戶(hù)端設(shè)計(jì)
在 CLI 客戶(hù)端定義一條通用命令 exec:
@ShellComponent
public class ExecCommand {
@ShellMethod(key = "exec", value = "執(zhí)行遠(yuǎn)程服務(wù)命令")
public String executeCommand(
@ShellOption(value = {"", "service"}, help = "服務(wù)名稱(chēng)") String serviceName,
@ShellOption(value = "--args", help = "命令參數(shù)", arity = 100) String[] args) {
// 構(gòu)建請(qǐng)求并發(fā)送到服務(wù)端
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 系統(tǒng)正常運(yùn)行
3. 服務(wù)端設(shè)計(jì)
服務(wù)端提供統(tǒng)一接口 /cli,根據(jù)服務(wù)名動(dòng)態(tài)分發(fā):
@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]);
// 動(dòng)態(tài)獲取 Service Bean
Object serviceBean = applicationContext.getBean(serviceName);
// 執(zhí)行命令
if (serviceBean instanceof CommandHandler handler) {
return handler.handle(args);
}
return "服務(wù)未找到";
}
}
4. 統(tǒng)一接口規(guī)范
所有需要通過(guò)CLI調(diào)用的服務(wù)都必須實(shí)現(xiàn) CommandHandler 接口:
public interface CommandHandler {
String handle(String[] args);
default String getDescription() { return "命令描述"; }
default String getUsage() { return "使用說(shuō)明"; }
}
示例服務(wù)實(shí)現(xiàn):
@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) {
// 實(shí)現(xiàn)獲取用戶(hù)列表邏輯
return "用戶(hù)列表...";
}
}
三、方案優(yōu)勢(shì)
1. 客戶(hù)端統(tǒng)一命令
- Shell 只需維護(hù)一條
exec命令 - 新增服務(wù)無(wú)需修改客戶(hù)端代碼
2. 服務(wù)端動(dòng)態(tài)分發(fā)
- 新增接口無(wú)需修改 CLI
- 統(tǒng)一接口入口便于權(quán)限控制與日志審計(jì)
3. 易擴(kuò)展
- 支持任意參數(shù)數(shù)量、類(lèi)型
- 可結(jié)合 OpenAPI 自動(dòng)生成命令提示與幫助信息
4. 邏輯解耦
- CLI 僅做命令解析和 HTTP 調(diào)用
- 業(yè)務(wù)邏輯完全在服務(wù)端
四、安全控制
1. 服務(wù)白名單
通過(guò)配置文件限制可訪問(wèn)的服務(wù):
cli:
allowed-services:
- userService
- roleService
- systemService
2. 參數(shù)驗(yàn)證
使用 Spring Validation 進(jìn)行請(qǐng)求參數(shù)校驗(yàn),防止惡意輸入。
3. 訪問(wèn)日志
記錄所有CLI調(diào)用,便于審計(jì)和問(wèn)題追蹤:
logger.info("CLI請(qǐng)求 - 服務(wù): {}, 參數(shù): {}, 來(lái)源: {}",
serviceName, Arrays.toString(args), httpRequest.getRemoteAddr());
五、實(shí)際應(yīng)用場(chǎng)景
1. 運(yùn)維場(chǎng)景
# 查看系統(tǒng)狀態(tài) exec systemService --args status # 重啟服務(wù) exec serviceManager --args restart userService # 查看日志 exec logService --args tail 100 error
2. 調(diào)試場(chǎng)景
# 查看用戶(hù)詳情 exec userService --args get 123 # 測(cè)試接口 exec testService --args simulate /api/orders # 清理緩存 exec cacheService --args clear all
3. 批量操作
# 批量導(dǎo)入用戶(hù) exec userService --args import users.csv # 批量更新角色 exec roleService --args batchUpdate role-mapping.json
六、擴(kuò)展功能
1. 交互增強(qiáng)
- Tab 補(bǔ)全:自動(dòng)補(bǔ)全服務(wù)名和參數(shù)
- 命令歷史:保存執(zhí)行歷史,支持上下鍵瀏覽
- 顏色輸出:不同類(lèi)型信息使用不同顏色顯示
2. 結(jié)果格式化
private String formatResponse(String data) {
try {
Object json = objectMapper.readValue(data, Object.class);
return objectMapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(json);
} catch (Exception e) {
return data;
}
}
3. 腳本模式
支持從文件執(zhí)行命令序列:
exec script --args commands.txt
七、總結(jié)
本文介紹的"通用命令+動(dòng)態(tài)分發(fā)"方案,通過(guò)Spring Boot + Spring Shell構(gòu)建,使用單一 exec 命令實(shí)現(xiàn)多服務(wù)動(dòng)態(tài)調(diào)用,大幅簡(jiǎn)化了CLI系統(tǒng)的維護(hù)復(fù)雜度。
到此這篇關(guān)于基于SpringBoot打造一個(gè)通用CLI命令系統(tǒng)的文章就介紹到這了,更多相關(guān)SpringBoot CLI命令系統(tǒng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
替換jar包中的yml,class等文件的實(shí)現(xiàn)方式
文章介紹了如何在不回退版本的情況下,替換jar包中的特定文件來(lái)修復(fù)線上bug,具體步驟包括:準(zhǔn)備文件、下載jar包、查看文件路徑、解壓文件、替換文件、重新打包文件、驗(yàn)證替換、重新上傳jar包并測(cè)試2025-12-12
Java中雙冒號(hào)運(yùn)算符(::)的用法詳解
在Java 8引入的Lambda表達(dá)式和函數(shù)式接口之后,雙冒號(hào)運(yùn)算符(::)成為了一項(xiàng)重要的功能,下面我們就來(lái)學(xué)習(xí)一下Java中的雙冒號(hào)運(yùn)算符及其常見(jiàn)應(yīng)用場(chǎng)景吧2023-12-12
Java在運(yùn)行時(shí)識(shí)別類(lèi)型信息的方法詳解
這篇文章主要給大家介紹了關(guān)于Java在運(yùn)行時(shí)識(shí)別類(lèi)型信息的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考借鑒,下面來(lái)一起看看吧2019-01-01
java從命令行獲取數(shù)據(jù)的三種方式代碼實(shí)例
這篇文章主要介紹了java從命令行獲取數(shù)據(jù)的三種方式代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
Java NoClassDefFoundError 異常及解決方法
本文主要介紹了Java NoClassDefFoundError 異常及解決方法,通過(guò)確保類(lèi)路徑配置正確、依賴(lài)庫(kù)完整、JDK 版本一致,并對(duì)動(dòng)態(tài)加載邏輯進(jìn)行充分測(cè)試,可以有效避免該錯(cuò)誤,感興趣的可以了解一下2025-05-05
Springboot整合minio實(shí)現(xiàn)文件服務(wù)的教程詳解
這篇文章主要介紹了Springboot整合minio實(shí)現(xiàn)文件服務(wù)的教程,文中的示例代碼講解詳細(xì),對(duì)我們的工作或?qū)W習(xí)有一定幫助,需要的可以參考一下2022-06-06
shiro多驗(yàn)證登錄代碼實(shí)例及問(wèn)題解決
這篇文章主要介紹了shiro多驗(yàn)證登錄代碼實(shí)例及問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12

