最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Shell開發(fā)自定義命令行工具詳解

 更新時(shí)間:2025年07月03日 08:12:46   作者:風(fēng)象南  
Spring Shell是Spring生態(tài)系統(tǒng)中的一個(gè)強(qiáng)大組件,它允許開發(fā)者輕松創(chuàng)建功能豐富的命令行,本文將介紹如何在Spring Boot項(xiàng)目中集成和使用Spring Shell,開發(fā)自定義命令,需要的可以了解下

Spring Shell是Spring生態(tài)系統(tǒng)中的一個(gè)強(qiáng)大組件,它允許開發(fā)者輕松創(chuàng)建功能豐富的命令行應(yīng)用程序。

當(dāng)與Spring Boot結(jié)合使用時(shí),可以快速構(gòu)建出專業(yè)級別的CLI工具。

本文將介紹如何在Spring Boot項(xiàng)目中集成和使用Spring Shell,開發(fā)自定義命令,以及一些高級特性。

1. Spring Shell簡介

Spring Shell是一個(gè)交互式shell框架,它提供了一種通過命令行與應(yīng)用程序交互的方式。

它支持自動(dòng)補(bǔ)全、幫助文檔生成、命令歷史和各種交互式功能,使命令行工具更加用戶友好。

Spring Shell的主要特性包括:

  • 類似于Bash的交互體驗(yàn)
  • Tab鍵自動(dòng)補(bǔ)全功能
  • 內(nèi)置幫助系統(tǒng)
  • 命令歷史記錄
  • 參數(shù)驗(yàn)證和轉(zhuǎn)換
  • 命令分組和可擴(kuò)展性

2. 在SpringBoot項(xiàng)目中集成Spring Shell

添加依賴

首先,在您的Spring Boot項(xiàng)目的pom.xml中添加Spring Shell依賴:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>demo</groupId>
    <artifactId>springboot-cmd</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.shell</groupId>
            <artifactId>spring-shell-starter</artifactId>
            <version>3.4.0</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.18</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.32</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>21</source>
                    <target>21</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

基本配置

@SpringBootApplication
public class ShellToolApplication {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(ShellToolApplication.class);
        application.setBannerMode(Banner.Mode.OFF);
        application.run(args);
    }

    @Bean
    public PromptProvider shellPromptProvider() {
        return () -> new AttributedString("boot-shell:>", AttributedStyle.DEFAULT.foreground(AttributedStyle.YELLOW));
    }

}
spring:
  shell:
    interactive:
      enabled: true

3. 開發(fā)自定義命令

在Spring Shell中,命令是通過在標(biāo)有@ShellComponent注解的類中創(chuàng)建標(biāo)有@ShellMethod注解的方法來定義的。

創(chuàng)建基本命令

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class MyCommands {
    
    @ShellMethod("顯示歡迎消息")
    public String hello(String name) {
        return "你好, " + name + "!";
    }
    
    @ShellMethod("計(jì)算兩個(gè)數(shù)字的和")
    public int add(int a, int b) {
        return a + b;
    }

    // 注意: 如果方法名為駝峰式命名,則shell使用時(shí)需要使用-分隔符,如addBig -> add-big
    @ShellMethod(value = "計(jì)算兩個(gè)數(shù)字的和,注意大小寫")
    public int addBig(int a, int b) {
        return a + b;
    }

    // 關(guān)于駝峰命名也可以使用key屬性進(jìn)行指定,則shell使用時(shí)仍然是駝峰式命名,如addSmall -> addSmall
    @ShellMethod(value = "計(jì)算兩個(gè)數(shù)字的和,注意大小寫",key = {"addSmall"})
    public int addSmall(int a, int b) {
        return a + b;
    }
}

使用上面的代碼,當(dāng)您運(yùn)行應(yīng)用程序時(shí),將可以使用helloadd命令:

shell:>hello 世界
你好, 世界!
shell:>add 5 3
8

命令參數(shù)配置

Spring Shell提供了豐富的參數(shù)配置選項(xiàng):

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;

@ShellComponent
public class AdvancedCommands {
    
    @ShellMethod("使用高級參數(shù)選項(xiàng)的示例")
    public String greet(
        @ShellOption(defaultValue = "世界") String name,
        @ShellOption(help = "決定是否使用大寫", defaultValue = "false") boolean uppercase
    ) {
        String greeting = "你好, " + name + "!";
        return uppercase ? greeting.toUpperCase() : greeting;
    }
}

4. 高級功能

命令分組

您可以使用@ShellCommandGroup注解對命令進(jìn)行分組:

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellCommandGroup;

@ShellComponent
@ShellCommandGroup("文件操作")
public class FileCommands {
    
    @ShellMethod("列出目錄內(nèi)容")
    public String ls(String path) {
        // 實(shí)現(xiàn)列出目錄內(nèi)容的邏輯
        return "列出 " + path + " 的內(nèi)容";
    }
    
    @ShellMethod("創(chuàng)建新目錄")
    public String mkdir(String dirName) {
        // 實(shí)現(xiàn)創(chuàng)建目錄的邏輯
        return "創(chuàng)建目錄: " + dirName;
    }
}

命令可用性控制

可以使用Availability來控制命令在不同情況下的可用性:

import org.springframework.shell.Availability;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellMethodAvailability;

@ShellComponent
public class SecurityCommands {
    
    private boolean loggedIn = false;
    
    @ShellMethod("登錄系統(tǒng)")
    public String login(String username, String password) {
        // 實(shí)現(xiàn)登錄邏輯
        this.loggedIn = true;
        return "用戶 " + username + " 已登錄";
    }
    
    @ShellMethod("查看敏感信息")
    public String query() {
        return "這是敏感信息,只有登錄后才能查看";
    }
    
    @ShellMethodAvailability("query")
    public Availability viewSecretInfoAvailability() {
        return loggedIn
            ? Availability.available()
            : Availability.unavailable("您需要先登錄才能查看敏感信息");
    }
}

自定義提示符

您可以通過實(shí)現(xiàn)PromptProvider接口來自定義提示符:

@Bean
public PromptProvider shellPromptProvider() {
    return () -> new AttributedString("my-shell:>", AttributedStyle.DEFAULT.foreground(AttributedStyle.YELLOW));
}

5. 一個(gè)工具示例

下面是一個(gè)更完整的示例,展示如何創(chuàng)建一個(gè)簡單的HTTP訪問CLI工具

package com.example.cmd.shell;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import org.springframework.shell.standard.ShellCommandGroup;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@ShellComponent
@ShellCommandGroup("HTTP請求")
public class HttpClientCommands {
    
    private String baseUrl = "";
    private final Map<String, String> headers = new HashMap<>();
    private final List<String> requestHistory = new ArrayList<>();
    
    @ShellMethod("設(shè)置基礎(chǔ)URL")
    public String setBaseUrl(String url) {
        this.baseUrl = url;
        return "基礎(chǔ)URL已設(shè)置為: " + url;
    }
    
    @ShellMethod("添加HTTP請求頭")
    public String addHeader(String name, String value) {
        headers.put(name, value);
        return "已添加請求頭: " + name + " = " + value;
    }
    
    @ShellMethod("清除所有HTTP請求頭")
    public String clearHeaders() {
        int count = headers.size();
        headers.clear();
        return "已清除 " + count + " 個(gè)請求頭";
    }
    
    @ShellMethod("顯示當(dāng)前配置")
    public String showConfig() {
        StringBuilder sb = new StringBuilder();
        sb.append("基礎(chǔ)URL: ").append(baseUrl).append("\n");
        sb.append("請求頭:\n");
        
        if (headers.isEmpty()) {
            sb.append("  (無)\n");
        } else {
            headers.forEach((name, value) -> 
                sb.append("  ").append(name).append(": ").append(value).append("\n"));
        }
        
        return sb.toString();
    }
    
    @ShellMethod("發(fā)送GET請求")
    public String get(
        @ShellOption(help = "請求的路徑或完整URL") String path,
        @ShellOption(help = "是否顯示響應(yīng)頭", defaultValue = "false") boolean showHeaders
    ) {
        String url = buildUrl(path);
        requestHistory.add("GET " + url);
        
        try {
            HttpRequest request = HttpRequest.get(url);
            addHeadersToRequest(request);
            
            HttpResponse response = request.execute();
            return formatResponse(response, showHeaders);
        } catch (Exception e) {
            return "請求失敗: " + e.getMessage();
        }
    }
    
    @ShellMethod("發(fā)送POST請求")
    public String post(
        @ShellOption(help = "請求的路徑或完整URL") String path,
        @ShellOption(help = "POST請求體(JSON)") String body,
        @ShellOption(help = "是否顯示響應(yīng)頭", defaultValue = "false") boolean showHeaders
    ) {
        String url = buildUrl(path);
        requestHistory.add("POST " + url);
        
        try {
            HttpRequest request = HttpRequest.post(url);
            addHeadersToRequest(request);
            
            // 添加Content-Type請求頭,如果沒有設(shè)置
            if (!headers.containsKey("Content-Type")) {
                request.header("Content-Type", "application/json");
            }
            
            HttpResponse response = request.body(body).execute();
            return formatResponse(response, showHeaders);
        } catch (Exception e) {
            return "請求失敗: " + e.getMessage();
        }
    }
    
    @ShellMethod("發(fā)送PUT請求")
    public String put(
        @ShellOption(help = "請求的路徑或完整URL") String path,
        @ShellOption(help = "PUT請求體(JSON)") String body,
        @ShellOption(help = "是否顯示響應(yīng)頭", defaultValue = "false") boolean showHeaders
    ) {
        String url = buildUrl(path);
        requestHistory.add("PUT " + url);
        
        try {
            HttpRequest request = HttpRequest.put(url);
            addHeadersToRequest(request);
            
            // 添加Content-Type請求頭,如果沒有設(shè)置
            if (!headers.containsKey("Content-Type")) {
                request.header("Content-Type", "application/json");
            }
            
            HttpResponse response = request.body(body).execute();
            return formatResponse(response, showHeaders);
        } catch (Exception e) {
            return "請求失敗: " + e.getMessage();
        }
    }
    
    @ShellMethod("發(fā)送DELETE請求")
    public String delete(
        @ShellOption(help = "請求的路徑或完整URL") String path,
        @ShellOption(help = "是否顯示響應(yīng)頭", defaultValue = "false") boolean showHeaders
    ) {
        String url = buildUrl(path);
        requestHistory.add("DELETE " + url);
        
        try {
            HttpRequest request = HttpRequest.delete(url);
            addHeadersToRequest(request);
            
            HttpResponse response = request.execute();
            return formatResponse(response, showHeaders);
        } catch (Exception e) {
            return "請求失敗: " + e.getMessage();
        }
    }
    
    @ShellMethod("下載文件")
    public String download(
        @ShellOption(help = "文件URL") String url,
        @ShellOption(help = "保存路徑") String savePath
    ) {
        requestHistory.add("DOWNLOAD " + url);
        
        try {
            long fileSize = HttpUtil.downloadFile(url, savePath);
            return "文件下載成功,大小: " + fileSize + " 字節(jié),保存路徑: " + savePath;
        } catch (Exception e) {
            return "文件下載失敗: " + e.getMessage();
        }
    }
    
    @ShellMethod("上傳文件")
    public String upload(
        @ShellOption(help = "上傳URL") String url,
        @ShellOption(help = "文件參數(shù)名") String paramName,
        @ShellOption(help = "文件路徑") String filePath,
        @ShellOption(help = "是否顯示響應(yīng)頭", defaultValue = "false") boolean showHeaders
    ) {
        requestHistory.add("UPLOAD " + url);
        
        try {
            HttpRequest request = HttpRequest.post(url);
            addHeadersToRequest(request);
            
            request.form(paramName, new java.io.File(filePath));
            HttpResponse response = request.execute();
            
            return formatResponse(response, showHeaders);
        } catch (Exception e) {
            return "文件上傳失敗: " + e.getMessage();
        }
    }
    
    @ShellMethod("格式化JSON")
    public String formatJson(
        @ShellOption(help = "JSON字符串") String json
    ) {
        try {
            return JSONUtil.formatJsonStr(json);
        } catch (Exception e) {
            return "JSON格式化失敗: " + e.getMessage();
        }
    }
    
    private String buildUrl(String path) {
        if (path.toLowerCase().startsWith("http")) {
            return path;
        }
        
        if (baseUrl.isEmpty()) {
            return path;
        }
        
        if (baseUrl.endsWith("/") && path.startsWith("/")) {
            return baseUrl + path.substring(1);
        } else if (!baseUrl.endsWith("/") && !path.startsWith("/")) {
            return baseUrl + "/" + path;
        } else {
            return baseUrl + path;
        }
    }
    
    private void addHeadersToRequest(HttpRequest request) {
        headers.forEach(request::header);
    }
    
    private String formatResponse(HttpResponse response, boolean showHeaders) {
        StringBuilder sb = new StringBuilder();
        sb.append("狀態(tài)碼: ").append(response.getStatus()).append("\n");
        
        if (showHeaders) {
            sb.append("響應(yīng)頭:\n");
            response.headers().forEach((name, values) -> {
                sb.append("  ").append(name).append(": ");
                sb.append(String.join(", ", values)).append("\n");
            });
            sb.append("\n");
        }
        
        sb.append("響應(yīng)體:\n");
        
        // 嘗試格式化JSON響應(yīng)體
        String body = response.body();
        if (body != null && !body.isEmpty()) {
            try {
                if (body.trim().startsWith("{") || body.trim().startsWith("[")) {
                    body = JSONUtil.formatJsonStr(body);
                }
            } catch (Exception ignored) {
                // 如果不是有效的JSON,直接使用原始響應(yīng)體
            }
        }
        
        sb.append(body);
        
        return sb.toString();
    }
}

6. 內(nèi)置命令清單

Spring Shell 默認(rèn)提供以下內(nèi)置命令,這些命令在應(yīng)用啟動(dòng)后可直接使用,無需額外配置。各命令功能及典型用法如下

命令名稱功能描述典型用法
help查看所有可用命令及其描述(輸入 help <命令>可查看特定命令詳情)shell:> help``shell:> help add
clear清空控制臺輸出(支持快捷鍵 Ctrl + L)shell:> clear
exit/ quit退出 Shell 應(yīng)用(二者功能相同)shell:> exit
script從文件批量執(zhí)行命令(需提供絕對路徑)shell:> script /tmp/commands.txt
stacktrace顯示最近一次異常的完整堆棧信息(默認(rèn)只顯示簡略錯(cuò)誤)發(fā)生異常后輸入:`shell:> stacktrace``
history顯示歷史命令shell:> history

到此這篇關(guān)于Spring Shell開發(fā)自定義命令行工具詳解的文章就介紹到這了,更多相關(guān)Spring Shell命令行內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mybatis-Plus3.2.0 MetaObjectHandler 無法進(jìn)行公共字段全局填充

    Mybatis-Plus3.2.0 MetaObjectHandler 無法進(jìn)行公共字段全局填充

    這篇文章主要介紹了Mybatis-Plus3.2.0 MetaObjectHandler 無法進(jìn)行公共字段全局填充,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • 一文詳解Java如何系統(tǒng)地避免空指針問題

    一文詳解Java如何系統(tǒng)地避免空指針問題

    新手Java開發(fā)總是經(jīng)??罩羔槞z查,甚至某些老手也會(huì)犯這樣的問題,所以這篇文章小編就帶大家一起來看看如何系統(tǒng)地避免空指針問題,希望對大家有所幫助
    2024-01-01
  • Springboot升級至2.4.0中出現(xiàn)的跨域問題分析及修改方案

    Springboot升級至2.4.0中出現(xiàn)的跨域問題分析及修改方案

    這篇文章主要介紹了Springboot升級至2.4.0中出現(xiàn)的跨域問題分析及修改方案,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • SpringCloud中的Feign遠(yuǎn)程調(diào)用接口傳參失敗問題

    SpringCloud中的Feign遠(yuǎn)程調(diào)用接口傳參失敗問題

    這篇文章主要介紹了SpringCloud中的Feign遠(yuǎn)程調(diào)用接口傳參失敗問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java修改Integer變量值遇到的問題及解決

    Java修改Integer變量值遇到的問題及解決

    這篇文章主要介紹了Java修改Integer變量值遇到的問題及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • SpringBoot?快速實(shí)現(xiàn)?api?接口加解密功能

    SpringBoot?快速實(shí)現(xiàn)?api?接口加解密功能

    在項(xiàng)目中,為了保證數(shù)據(jù)的安全,我們常常會(huì)對傳遞的數(shù)據(jù)進(jìn)行加密,Spring?Boot接口加密,可以對返回值、參數(shù)值通過注解的方式自動(dòng)加解密,這篇文章主要介紹了SpringBoot?快速實(shí)現(xiàn)?api?接口加解密功能,感興趣的朋友一起看看吧
    2023-10-10
  • java中map和對象互轉(zhuǎn)工具類的實(shí)現(xiàn)示例

    java中map和對象互轉(zhuǎn)工具類的實(shí)現(xiàn)示例

    這篇文章主要介紹了java中map和對象互轉(zhuǎn)工具類的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Spring 方法注解@Bean及配置類掃描路徑

    Spring 方法注解@Bean及配置類掃描路徑

    這篇文章主要介紹了Spring 方法注解@Bean及配置類掃描路徑,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2024-12-12
  • kotlin改善java代碼實(shí)例分析

    kotlin改善java代碼實(shí)例分析

    我們給大家整理了關(guān)于kotlin改善java代碼的相關(guān)實(shí)例以及操作的詳細(xì)方法,有需要的讀者們參考下。
    2018-03-03
  • SpringBoot自定義線程池,執(zhí)行定時(shí)任務(wù)方式

    SpringBoot自定義線程池,執(zhí)行定時(shí)任務(wù)方式

    這篇文章主要介紹了SpringBoot自定義線程池,執(zhí)行定時(shí)任務(wù)方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04

最新評論

永新县| 甘肃省| 陆川县| 启东市| 伊川县| 清镇市| 城固县| 连山| 五大连池市| 腾冲县| 皋兰县| 娱乐| 柯坪县| 平乡县| 临泉县| 大港区| 札达县| 恩平市| 化州市| 东台市| 志丹县| 夹江县| 汶上县| 博兴县| 利津县| 大荔县| 商城县| 改则县| 定远县| 盐津县| 邹平县| 大渡口区| 缙云县| 砚山县| 喀什市| 南丰县| 三台县| 东光县| 宜黄县| 昌乐县| 奉节县|