Spring Shell 中文文檔

spring shell

官方鏈接

Version 2.0.0.RELEASE

可以參考這篇文章的視頻簡介: spring shell Java命令行集成

What is Spring Shell?

不是所有的應用程序都需要一個花哨的 web 用戶界面!有時,使用交互式終端與應用程序交互是完成工作的最合適方式。


Spring Shell 允許您輕鬆創建這樣一個可運行的應用程序,用戶將在其中輸入文本命令,這些命令將被執行,直到程序終止。 The Spring Shell project provides the infrastructure to create such a REPL (Read, Eval, Print Loop), 允許開發人員使用熟悉的 Spring 編程模型專注於命令實現。


高級功能,如解析、 TAB 自動補全、輸出顏色化、奇特的 ascii-art 表顯示、輸入轉換和驗證,都免費提供, 開發人員只需關注核心命令邏輯。(Advanced features such as parsing, TAB completion, colorization of output, fancy ascii-art table display, input conversion and validation all come for free, with the developer only having to focus on core command logic.)


Using Spring Shell

Getting Started

爲了瞭解 Spring Shell 提供了什麼,讓我們編寫一個簡單的 shell 應用程序,它有一個簡單的命令將兩個數字加在一起。

Let’s Write a Simple Boot App

從版本 2 開始,Spring Shell 已經從頭開始重寫,並考慮到各種增強功能,其中之一是易於與 Spring Boot 集成,儘管這不是一個強有力的要求。爲了本教程的目的,讓我們創建一個簡單的引導應用程序,例如使用 start.spring.io。這個最小的應用程序只依賴於 spring-boot-starter,並配置 spring-boot-maven-plugin,生成一個可執行的 jar:

Adding a Dependency on Spring Shell

<dependency>
    <groupId>org.springframework.shell</groupId>
    <artifactId>spring-shell-starter</artifactId>
    <version>2.0.0.RELEASE</version>
</dependency>
 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

Your first command

是時候添加我們的第一個命令了。創建一個新類 (隨心所欲地命名它),and annotate it with @ShellComponent (@Component的變體,用於限制掃描候選命令的類集).


然後,創建一個 add 方法,該方法接受兩個整數 (a 和 b) 並返回它們的總和. Annotate it with @ShellMethod and 並在@ShellMethod 中提供命令的描述:

package com.example.demo;

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

@ShellComponent
public class MyCommands {

    @ShellMethod("Add two integers together.")
    public int add(int a, int b) {
        return a + b;
    }
}

Let’s Give It a Ride!

mvn clean install  -DskipTests
java -jar target/demo-0.0.1-SNAPSHOT.jar

您將會受到以下屏幕的歡迎 (橫幅來自 Spring Boot,可以像往常一樣定製):

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot

shell:>

下面是 shell:> 提示,輸入 入 add 1 2 即可體驗

shell:>add 1 2
3

嘗試玩 shell (提示: 有一個help 命令),完成後,鍵入exit ENTER。
本文檔的其餘部分深入研究了整個 Spring Shell 編程模型。


Writing your own Commands

(spring 將方法轉換爲可執行的shell 是可插拔的)The way Spring Shell decides to turn a method into an actual shell command is entirely pluggable (see Extending Spring Shell), but as of Spring Shell 2.x, the recommended way to write commands is to use the new API described in this section (the so-called standard API).(但是從 Spring Shell 2.X 開始,編寫命令的推薦方法是使用本節中描述的新 API (所謂的標準 API)。)

使用標準 API,beans 上的方法將被轉換成可執行命令,前提是

  • the bean class bears the @ShellComponent annotation.這用於限制考慮的 bean 集。
  • the method bears the @ShellMethod annotation.

The @ShellComponent is a stereotype annotation itself meta-annotated with @Component. As such, it can be used in addition to the filtering mechanism to also declare beans (e.g. using @ComponentScan).

可以使用註釋的 value 屬性定製創建的 bean 的名稱

It’s all about Documentation!

@ShellMethod 註釋唯一需要的屬性是它的 value 屬性,該屬性應該用於寫一個簡短的、一句話的命令操作描述。)(see Integrated Documentation with the help Command).

您的命令說明應該簡短,只能是一兩個句子。爲了獲得更好的一致性,建議以大寫字母開頭,以點結束。

Customizing the Command Name(s)

默認情況下,不需要爲命令指定key (i.e. the word(s) that should be used to invoke it in the shell這個是在調用shell的時候被應用)。方法的名稱將用作命令的key, turning camelCase names into dashed, gnu-style, names (that is, sayHello() will become say-hello). 名稱默認請求使用方法的名稱sayHello 轉換爲shell 就是 say-hello

可以使用註釋的 key 屬性顯式設置命令鍵,如下所示:

@ShellMethod(value = "Add numbers.", key = "sum")
public int add(int a, int b) {
    return a + b;
}

Key 屬性接受多個值。如果爲單個方法設置多個鍵,則命令將使用這些不同的別名註冊。


Invoking your Commands

By Name vs. Positional Parameters(按名稱與位置參數)

如上所述,用 @ shellmethod 裝飾方法是創建命令的唯一要求。這樣做時,用戶可以通過兩種可能的方式設置所有方法參數的值:

  • using a parameter key (e.g. --arg value). 這種方法被稱爲 “按名稱” 參數
  • or without a key, simply setting parameter values in the same order they appear in the method signature (“positional” parameters). 或者沒有鍵,只需按照參數值在方法簽名 (“位置” 參數) 中出現的順序設置參數值。

這兩種方法可以混合和匹配,命名參數總是優先 (因爲它們不太容易模糊)。因此,給定以下命令

@ShellMethod("Display stuff.")
public String echo(int a, int b, int c) {
    return String.format("You said a=%d, b=%d, c=%d", a, b, c);
}

shell:>echo 1 2 3            //This uses positional parameters   
You said a=1, b=2, c=3

shell:>echo --a 1 --b 2 --c 3    //This is an example of full by-name parameters
You said a=1, b=2, c=3

shell:>echo --b 2 --c 3 --a 1   //By-name parameters can be reordered as desired
You said a=1, b=2, c=3

shell:>echo --a 1 2 3         //you can use a mix of the two approaches  
You said a=1, b=2, c=3

shell:>echo 1 --c 3 2       //	The non by-name parameters are resolved in the order they appear    
You said a=1, b=2, c=3

Customizing the Named Parameter Key(s) 自定義命名參數鍵

如上所述,導出命名參數的key的默認策略是使用方法簽名的 java 名稱,並用兩個破折號 (–) 作爲前綴。這可以通過兩種方式定製:

  • 更改整個方法的默認前綴, use the prefix() attribute of the @ShellMethod annotation
  • 以每個參數的方式覆蓋整個key , annotate the parameter with the @ShellOption annotation.
@ShellMethod(value = "Display stuff.", prefix="-")
public String echo(int a, int b, @ShellOption("--third") int c) {
    return String.format("You said a=%d, b=%d, c=%d", a, b, c);
}

對於這種設置,可能的參數鍵將是-a 、-b 和 --third。

可以爲一個參數指定幾個鍵。如果是這樣,這些將是指定相同參數的互斥方式 (因此只能使用其中一個) here is the signature of the built-in help command:

@ShellMethod("Describe a command.")
public String help(@ShellOption({"-C", "--command"}) String command) {
    ...
 }

Optional Parameters and Default Values (默認值)

Spring Shell 提供了給參數默認值的能力,這將允許用戶省略這些參數:

@ShellMethod("Say hello.")
public String greet(@ShellOption(defaultValue="World") String who) {
    return "Hello " + who;
}

Parameter Arity(參數相關性)

到目前爲止,一直假設每個參數映射到用戶輸入的單個單詞。然而,當參數值應該是多值時,可能會出現這種情況。 This is driven by the arity() attribute of the @ShellOption annotation. 只需爲參數類型使用集合或數組,並指定預期的值.

@ShellMethod("Add Numbers.")
public float add(@ShellOption(arity=3) float[] numbers) {
    return numbers[0] + numbers[1] + numbers[2];
}

然後,可以使用以下任何語法調用該命令:

shell:>add 1 2 3.3
6.3
shell:>add --numbers 1 2 3.3
6.3

Infinite Arity(無限度)

要執行

Special Handling of Boolean Parameters(布爾參數的特殊處理)

默認值爲false

@ShellMethod("Terminate the system.")
public String shutdown(boolean force) {
  return "You said " + force;
} 

這允許以下調用:

shell:>shutdown
You said false
shell:>shutdown --force
You said true

具有隱式 arity()=0的這種行爲阻止用戶指定值 (e.g. shutdown --force true). If you would like to allow this behavior (and forego the flag approach), then force an arity of 1 using the annotation:

@ShellMethod("Terminate the system.")
public String shutdown(@ShellOption(arity=1, defaultValue="false") boolean force) {
    return "You said " + force;
}

Quotes Handling(引用處理)

Spring Shell 接受用戶輸入並用文字標記它,在空格字符上拆分。如果用戶想要提供包含空格的參數值,則需要引用該值。支持單引號 (’) 和雙引號 ("),這些引號不會是值的一部分:

@ShellMethod("Prints what has been entered.")
public String echo(String what) {
    return "You said " + what;
}
shell:>echo Hello
You said Hello
shell:>echo 'Hello'
You said Hello
shell:>echo 'Hello World'
You said Hello World
shell:>echo "Hello World"
You said Hello World

支持單引號和雙引號允許用戶輕鬆地將一種類型的引號嵌入到值中:

shell:>echo "I'm here!"
You said I'm here!
shell:>echo 'He said "Hi!"'
You said He said "Hi!"

如果用戶需要嵌入用於引用整個參數的相同類型的引用,轉義序列將使用反斜槓 () 字符:

shell:>echo 'I\'m here!'
You said I'm here!
shell:>echo "He said \"Hi!\""
You said He said "Hi!"
shell:>echo I\'m here!
You said I'm here!

Interacting with the Shell

The Spring Shell project builds on top of the JLine library,因此,帶來了許多不錯的互動功能,其中一些在本節中詳細介紹。

首先,Spring Shell 幾乎在任何可能的地方都支持 TAB 完成。因此,如果有一個 echo 命令,用戶按下 e,c,TAB,echo 就會出現。如果有幾個命令以 ec 開頭,則系統會提示用戶選擇 (使用 TAB 或 Shift + TAB 導航,並輸入進行選擇。))

除了命令之外還支持參數. It also works for parameter keys (--arg) and even parameter values, if the application developer registered the appropriate beans (see Providing TAB Completion Proposals).

Spring Shell 應用程序的另一個很好的功能是支持線路延續。如果命令及其參數太長,並且在屏幕上不太合適,用戶可以用反斜槓 () 塊它並終止一行字符然後點擊 ENTER 並繼續下一行。Uppon 提交整個命令,這將被解析,就像用戶在換行符上輸入了一個空格一樣。

shell:>register module --type source --name foo  \ 
> --uri file:///tmp/bar
Successfully registered module 'source:foo'

最後,Spring Shell 應用程序受益於許多鍵盤快捷鍵,在使用從 Emacs 借來的常規操作系統 Shell 時,您可能已經熟悉了這些快捷鍵。值得注意的快捷方式包括 Ctrl + r 執行反向搜索,Ctrl + a 和 Ctrl + e 分別移動到行的開始和結束,或者 Esc f 和 Esc b 向前移動 (resp。向後) 一個單詞一次。

Providing TAB Completion Proposals

Validating Command Arguments (驗證命令參數)

Spring Shell 與 Bean 驗證 API 集成,以支持對命令參數的自動和自記錄約束。

命令參數上的註釋以及方法級別的註釋將被遵守,並在命令執行之前觸發驗證。給出以下命令:

@ShellMethod("Change password.")
public String changePassword(@Size(min = 8, max = 40) String password) {
	return "Password successfully set to " + password;
}

shell:>change-password hello
The following constraints were not met:
	--password string : size must be between 8 and 40 (You passed 'hello')

Dynamic Command Availability(動態命令可用性)

由於應用程序的內部狀態,註冊的命令有時可能沒有意義。例如,也許有一個下載命令,但是隻有當用戶在遠程服務器上使用 connect 後,它纔會工作。現在,如果用戶嘗試使用下載命令,shell 應該優雅地解釋該命令確實存在,但是它當時不可用。Spring Shell 允許開發人員這樣做,甚至對命令不可用的原因提供了簡短的解釋。

命令有三種可能的方式來表示可用性。他們都利用了返回可用性實例的無 arg 方法。讓我們從一個簡單的例子開始:

@ShellComponent
public class MyCommands {
    private boolean connected;
    @ShellMethod("Connect to the server.")
    public void connect(String user, String password) {
        [...]
        connected = true;
    }
    @ShellMethod("Download the nuclear codes.")
    public void download() {
        [...]
    }
    public Availability downloadAvailability() {
        return connected
            ? Availability.available()
            : Availability.unavailable("you are not connected");
    }
}

其名稱中有可用性後綴。該方法返回可用性實例,使用兩種工廠方法之一構建.download+Availability

集成幫助中也利用了有關當前不可用命令的信息。請參見幫助命令的集成文檔。 Integrated Documentation with the help Command.

如果由於某種原因,以命令方法的名稱命名可用性方法不適合您,您可以使用 @ShellMethodAvailability可用性提供顯式名稱,如下所示:

    @ShellMethod("Download the nuclear codes.")
    @ShellMethodAvailability("availabilityCheck") 
    public void download() {
        [...]
    }

    public Availability availabilityCheck() { 
        return connected
            ? Availability.available()
            : Availability.unavailable("you are not connected");
    }

Lastly, it is often the case that several commands in the same class share the same internal state and thus should all be available or unavailable all at one. Instead of having to stick the @ShellMethodAvailability on all command methods, Spring Shell allows the user to flip things around and put the @ShellMethodAvailabilty annotation on the availability method, specifying the names of the commands that it controls:

最後,通常情況下,同一個類中的幾個命令共享相同的內部狀態,因此都應該同時可用或不可用。Spring Shell 不需要在所有命令方法上粘上 @ShellMethodAvailability,而是允許用戶翻轉東西,並在可用性方法上放置 @ShellMethodAvailabilty , 指定它控制的命令的名稱:

@ShellMethod("Download the nuclear codes.")
public void download() {
    [...]
}

@ShellMethod("Disconnect from the server.")
public void disconnect() {
    [...]
}

@ShellMethodAvailability({"download", "disconnect"})
public Availability availabilityCheck() {
    return connected
        ? Availability.available()
        : Availability.unavailable("you are not connected");
}

Organizing Commands (命令分組)

當你的 shell 開始提供很多功能時,你可能會得到很多命令,這可能會讓你的用戶感到困惑。鍵入 help 他們會看到一系列令人生畏的命令,按字母順序排列,這可能並不總是有意義的。

爲了緩解這種情況,Spring Shell 提供了將命令組合在一起的能力,並提供了合理的默認值。然後,相關命令將最終在同一個組中 (例如用戶管理命令),並一起顯示在幫助屏幕和其他地方。

默認情況下,命令將根據它們實現的類分組,將駱駝案例類名稱轉換成單獨的單詞。

但是,如果此行爲不適合您,您可以按照優先級順序,以以下方式覆蓋命令的組:

  • specifying a group() in the @ShellMethod annotation
  • placing a @ShellCommandGroup on the class the command is defined in. This will apply the group for all commands defined in that class (unless overridden as above)
  • placing a @ShellCommandGroup on the package (via package-info.java) the command is defined in. This will apply to all commands defined in the package (unless overridden at the method or class level as explained above)
public class UserCommands {
    @ShellCommand(value = "This command ends up in the 'User Commands' group")
    public void foo() {}

    @ShellCommand(value = "This command ends up in the 'Other Commands' group",
            group = "Other Commands")
    public void bar() {}
}

...

@ShellCommandGroup("Other Commands")
public class SomeCommands {
        @ShellMethod(value = "This one is in 'Other Commands'")
        public void wizz() {}

        @ShellMethod(value = "And this one is 'Yet Another Group'",
                group = "Yet Another Group")
        public void last() {}
}

Built-In Commands(內置命令)

都附帶一組內置命令。這些命令可以單獨被覆蓋或禁用 (see Overriding or Disabling Built-In Commands)

ntegrated Documentation with the help Command

運行 shell 應用程序通常意味着用戶處於圖形有限的環境中。儘管在手機時代,我們總是連接在一起,但訪問 web 瀏覽器或任何其他富 UI 應用程序 (如 pdf viewer) 可能並不總是可能的。這就是爲什麼正確地自我記錄 shell 命令很重要,這就是 help 命令的來源。



鍵入 help + ENTER 將向 shell 列出所有已知命令 (包括不可用命令),並簡要描述它們的工作:

shell:>help
AVAILABLE COMMANDS
        add: Add numbers together.
      * authenticate: Authenticate with the system.
      * blow-up: Blow Everything up.
        clear: Clear the shell screen.
        connect: Connect to the system
        disconnect: Disconnect from the system.
        exit, quit: Exit the shell.
        help: Display help about available commands.
        register module: Register a new module.
        script: Read and execute commands from a file.
        stacktrace: Display the full stacktrace of the last error.
Commands marked with (*) are currently unavailable.
Type `help <command>` to learn more.

鍵入 help 將顯示有關命令的更詳細信息,包括可用參數、它們的類型以及它們是否是強制性的,等等。

shell:>help help


NAME
	help - Display help about available commands.

SYNOPSYS
	help [[-C] string]

OPTIONS
	-C or --command  string
		The command to obtain help for.  [Optional, default = <none>]

Clearing the Screen

Clear 命令執行您期望的操作,並清除屏幕,重置左上角的提示。

Exitting the Shell

退出命令quit (也別名爲exit) 只需請求 shell 退出,優雅地關閉 Spring 應用程序上下文。如果沒有被覆蓋,JLine 歷史記錄 bean 將編寫所有執行到磁盤的命令的歷史記錄.

Displaying Details about an Error

當命令代碼中出現異常時,它被 shell 捕獲,並顯示一條簡單的單行消息,以免過多的信息溢出用戶。儘管在某些情況下,瞭解到底發生了什麼很重要 (尤其是如果異常有嵌套原因)。
爲此,Spring Shell 記得最後一次發生的異常,用戶以後可以使用 stacktrace 命令在控制檯上打印所有血淋淋的詳細信息。

Running a Batch of Commands(運行一批命令)

The script命令接受本地文件作爲參數,並將一次重放在那裏找到的命令。
從文件中讀取的行爲與交互式 shell 中的行爲完全一樣,因此以//開頭的行將被視爲註釋並被忽略,而以 \ 結尾的行將觸發行繼續。


Customizing the Shell

Overriding or Disabling Built-In Commands

Spring Shell 提供了內置命令,以實現許多 (如果不是所有的 shell 應用程序) 需要的日常任務。如果您對它們的行爲方式不滿意,您可以禁用或覆蓋它們,如本節所述。

Disabling all Built-in Commands
如果你根本不需要內置命令,那麼有一個簡單的方法來 “禁用” 它們: 只是不包括它們!要麼在spring-shell-standard-commands使用 maven 排除,要麼,如果你有選擇地包括 Spring Shell 依賴項,不要引入它!

<dependency>
    <groupId>org.springframework.shell</groupId>
    <artifactId>spring-shell-starter</artifactId>
    <version>2.0.0.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.shell</groupId>
            <artifactId>spring-shell-standard-commands</artifactId>
        </exclusion>
    </exclusion>
</dependency>

Disabling Specific Commands

要禁用單個內置命令,只需在應用程序環境中將 spring.shell.command..Enable 屬性設置爲 false。這樣做的一個簡單方法是在 main () 入口點向引導應用程序傳遞額外的參數:

        public static void main(String[] args) throws Exception {
                String[] disabledCommands = {"--spring.shell.command.help.enabled=false"}; 
                String[] fullArgs = StringUtils.concatenateStringArrays(args, disabledCommands);
                SpringApplication.run(MyApp.class, fullArgs);
        }

Overriding Specific Commands

如果您不希望禁用命令,而是希望提供自己的實現,那麼您可以

  • 禁用上面解釋的命令,並使用相同的名稱註冊您的實現
  • have your implementing class implement the .Command interface.
public class MyClear implements Clear.Command {

    @ShellCommand("Clear the screen, only better.")
    public void clear() {
        // ...
    }
}

ResultHandlers

PromptProvider

每次命令調用後,shell 都會等待用戶的新輸入,顯示黃色提示:

可以通過註冊類型爲 PromptProvider 的 bean 來定製這種行爲。這樣的 bean 可以使用內部狀態來決定向用戶顯示什麼 (例如,它可以對應用程序事件做出反應),並且可以使用 JLine 的屬性字符序列來顯示花哨的 ANSI 文本。

@Component
public class CustomPromptProvider implements PromptProvider {

        private ConnectionDetails connection;

        @Override
        public AttributedString getPrompt() {
                if (connection != null) {
                        return new AttributedString(connection.getHost() + ":>",
                                AttributedStyle.DEFAULT.foreground(AttributedStyle.YELLOW));
                }
                else {
                        return new AttributedString("server-unknown:>",
                                AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
                }
        }

        @EventListener
        public void handle(ConnectionUpdatedEvent event) {
                this.connection = event.getConnectionDetails();
        }
}

Customizing Command Line Options Behavior(自定義命令行選項行爲)

Spring Shell 附帶兩個默認的 Spring Boot 應用程序:

  • InteractiveShellApplicationRunner bootstraps the Shell REPL. It sets up the JLine infrastructure and eventually calls Shell.run()
  • ScriptShellApplicationRunner looks for program arguments that start with @, assumes those are local file names and tries to run commands contained in those files (with the same semantics as the script command) and then exits the process (by effectively disabling the InteractiveShellApplicationRunner, see below).

如果這種行爲不適合您,只需提供一個 (或多個) application ationrunner 類型的 bean,並可選地禁用標準 bean。

@Order(InteractiveShellApplicationRunner.PRECEDENCE - 100) // Runs before InteractiveShellApplicationRunner
public class ScriptShellApplicationRunner implements ApplicationRunner {

        @Override
        public void run(ApplicationArguments args) throws Exception {
                List<File> scriptsToRun = args.getNonOptionArgs().stream()
                                .filter(s -> s.startsWith("@"))
                                .map(s -> new File(s.substring(1)))
                                .collect(Collectors.toList());

                boolean batchEnabled = environment.getProperty(SPRING_SHELL_SCRIPT_ENABLED, boolean.class, true);

                if (!scriptsToRun.isEmpty() && batchEnabled) {
                        InteractiveShellApplicationRunner.disable(environment);
                        for (File file : scriptsToRun) {
                                try (Reader reader = new FileReader(file);
                                                FileInputProvider inputProvider = new FileInputProvider(reader, parser)) {
                                        shell.run(inputProvider);
                                }
                        }
                }
        }

Customizing Arguments Conversion(自定義參數轉換)

從文本輸入到實際方法參數的轉換使用標準的 Spring 轉換機制。 Spring Shell installs a new DefaultConversionService (with built-in converters enabled) and registers to it any bean of type Converter<S, T>, GenericConverter or ConverterFactory<S, T> that it finds in the application context.


這意味着定製到 Foo 類型的自定義對象的轉換非常容易: just install a Converter<String, Foo> bean in the context.

@ShellComponent
class ConversionCommands {

        @ShellMethod("Shows conversion using Spring converter")
        public String conversionExample(DomainObject object) {
                return object.getClass();
        }

}

class DomainObject {
        private final String value;

        DomainObject(String value) {
                this.value = value;
        }

        public String toString() {
                return value;
        }
}

@Component
class CustomDomainConverter implements Converter<String, DomainObject> {

        @Override
        public DomainObject convert(String source) {
                return new DomainObject(source);
        }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章