
本文檔的副本可能供您自己使用和分發(fā)至 其他,前提是您不對此類副本和進一步 前提是每個副本都包含此版權聲明,無論是否在 打印或電子。
并非所有應用程序都需要花哨的 Web 用戶界面。 有時,通過交互式終端與應用程序交互是 完成任務的最合適方法。
Spring Shell 允許您創(chuàng)建這樣一個可運行的應用程序,其中 用戶輸入在程序終止之前運行的文本命令。 Spring Shell 項目提供了創(chuàng)建此類 REPL 的基礎設施(Read, Eval, 打印循環(huán))應用程序,讓您專注于通過使用 熟悉的 Spring 編程模型。
(資料圖)
Spring Shell 包括高級功能(例如解析、制表符補全、著色 輸出,花哨的ASCII藝術表格顯示,輸入轉(zhuǎn)換和驗證),解放您 專注于核心命令邏輯。
Spring Shell 2.1.x 是一個重大的返工,使代碼庫與時俱進 現(xiàn)有的 Spring 引導版本,添加新功能,特別是 使其與GraalVM一起使用,這使得命令行應用程序變得很多 在 Java 空間中更相關。遷移到新的主要版本還可以讓 我們清理代碼庫并進行一些必要的中斷性更改。 |
為了了解Spring Shell提供了什么,我們可以編寫一個簡單的shell應用程序 有一個簡單的命令來添加兩個數(shù)字。
從版本 2 開始,Spring Shell 已經(jīng)從頭開始重寫了各種 考慮到增強功能,其中之一是與Spring Boot的輕松集成。
出于本教程的目的,我們通過以下方式創(chuàng)建一個簡單的 Boot 應用程序 使用start.spring.io。這個最小的應用程序僅依賴并配置生成可執(zhí)行的 über-jar:??spring-boot-starter?
???spring-boot-maven-plugin?
?
org.springframework.boot spring-boot-starter
開始使用Spring Shell的最簡單方法是依賴工件。 它配備了使用Spring Shell所需的一切,并且可以很好地與Boot配合使用, 根據(jù)需要僅配置必要的 bean:??{starter-artifactId}?
?
org.springframework.shell spring-shell-starter 2.1.4
鑒于 Spring Shell 啟動 REPL(讀取-評估-打印-循環(huán))是因為存在這種依賴關系, 在本教程中,您需要在構建 () 時跳過測試,或者刪除示例集成測試 這是由 start.spring.io?產(chǎn)生的。如果不刪除它,集成測試將創(chuàng)建 Spring 和,根據(jù)您的構建工具,卡在評估循環(huán)中或與 NPE 一起崩潰。? |
Now we can add our first command. To do so, create a new class (named whatever you want) and annotate it with(a variation ofthat is used to restrict the set of classes that are scanned for candidate commands).??@ShellComponent?
???@Component?
?
Then we can create anmethod that takes two ints (and) and returns their sum. We need to annotate it withand provide a description of the command in the annotation (the only piece of information that is required):??add?
???a?
???b?
???@ShellMethod?
?
package com.example.demo;import org.springframework.shell.standard.ShellMethod;import org.springframework.shell.standard.ShellComponent;@ShellComponentpublic class MyCommands { @ShellMethod("Add two integers together.") public int add(int a, int b) { return a + b; }}
要構建應用程序并運行生成的 jar,請運行以下命令:
./mvnw clean install -DskipTests[...]java -jar target/demo-0.0.1-SNAPSHOT.jar
shell:>
黃色提示符邀請您鍵入命令。鍵入、按下并欣賞魔法:??shell:>?
???add 1 2?
???ENTER?
?
shell:>add --a 1 --b 23
你應該玩 shell(提示:有一個命令)。完成后,鍵入并按 。??help?
???exit?
???ENTER?
?
本文檔的其余部分將深入探討整個 Spring Shell 編程模型。
本節(jié)介紹 Spring Shell 的基礎知識。在繼續(xù)定義實際命令和選項之前, 我們需要了解Spring Shell的一些基本概念。
從本質(zhì)上講,在擁有正常工作的 Spring Shell 應用程序之前,需要做一些事情:
創(chuàng)建一個 Spring Boot 應用程序。定義命令和選項。打包應用程序。以交互方式或非交互方式運行應用程序。您可以獲得一個完整的 Spring Shell 應用程序,而無需定義任何用戶級命令 因為提供了一些基本的內(nèi)置命令(例如 和 )。??help?
???history?
?
在本文檔中,我們引用了使用 注釋(主要涉及 和 的使用)和 編程方式(使用 )。? 編程模型是實際注冊事物的方式,即使您使用注釋也是如此。 和注釋是舊版功能 我們還不想刪除。 是新的發(fā)展 添加新功能的模型。我們很可能會取代現(xiàn)有的 帶有更好內(nèi)容的注釋,以支持模型中的新功能。? |
在本節(jié)中,我們將介紹實際的命令注冊并保留命令選項 并在稍后的文檔中執(zhí)行。可以在命令注冊中找到更多詳細信息。
有兩種不同的方法來定義命令:通過注釋模型和 通過程序化模型。在注釋模型中,定義方法 在類中,并使用特定注釋批注類和方法。在程序化模型中, 使用更低級的方法,定義命令注冊( 作為 Bean 或通過動態(tài)注冊到命令目錄)。
當您使用標準 API 時,bean 上的方法將轉(zhuǎn)換為可執(zhí)行命令,前提是:
Bean 類帶有注釋。(這用于限制豆子集 被考慮在內(nèi)。@ShellComponent
該方法帶有注釋。@ShellMethod
這是一個刻板印象注釋,它本身是用 進行元注釋的。結果, 除了過濾機制之外,還可以使用它來聲明 bean(例如,通過使用 )。? 您可以使用注釋的屬性自定義所創(chuàng)建 Bean 的名稱。? |
@ShellComponentstatic class MyCommands { @ShellMethod public void mycommand() { }}
注釋唯一必需的屬性是其屬性,它應該具有 對命令功能的簡短、一句話的描述。這讓您的用戶 獲取有關命令的一致幫助,而無需離開 shell(請參閱幫助)。??@ShellMethod?
???value?
?
命令的描述應該簡短——不超過一兩句話。為了更好 一致性,它應該以大寫字母開頭,以句點結尾。 |
默認情況下,無需指定命令的鍵(即應使用的單詞) 在命令行管理程序中調(diào)用它)。方法名稱用作命令鍵,將駝峰大小寫名稱轉(zhuǎn)換為 虛線、GNU 樣式的名稱(例如,變?yōu)?)。??sayHello()?
???say-hello?
?
但是,您可以使用批注的屬性顯式設置命令鍵:??key?
?
@ShellMethod(value = "Add numbers.", key = "sum")public int add(int a, int b) { return a + b;}
該屬性接受多個值。 如果為單個方法設置多個鍵,則會使用這些不同的別名注冊該命令。? |
命令鍵幾乎可以包含任何字符,包括空格。不過,在想出名字時, 請記住,一致性通常受到用戶的贊賞。也就是說,您應該避免將虛線名稱與 間隔名稱和其他不一致之處。 |
在編程模型中,定義為 ,并自動注冊:??CommandRegistration?
???@Bean?
?
@BeanCommandRegistration commandRegistration() { return CommandRegistration.builder() .command("mycommand") .build();}
當你的 shell 開始提供很多功能時,你最終可能會 有很多命令,這可能會讓您的用戶感到困惑。通過鍵入 , 他們會看到一個令人生畏的命令列表,按字母順序組織, 這可能并不總是顯示可用命令的最佳方式。??help?
?
為了減輕這種可能的混淆,Spring Shell 提供了將命令組合在一起的功能, 具有合理的默認值。然后,相關命令將在同一組中結束(例如,) 并一起顯示在幫助屏幕和其他地方。??User Management Commands?
?
默認情況下,命令根據(jù)實現(xiàn)它們的類進行分組, 將駝峰類名轉(zhuǎn)換為單獨的單詞(因此變?yōu)?)。 這是一個明智的默認值,因為無論如何,相關命令通常已經(jīng)在類中, 因為他們需要使用相同的協(xié)作對象。??URLRelatedCommands?
???URL Related Commands?
?
但是,如果此行為不適合您,則可以覆蓋該組 按優(yōu)先級順序按以下方式命令:
在批注中指定 a。group()
@ShellMethod
在定義命令的類上放置 a。這適用 該類中定義的所有命令的組(除非被重寫,如前所述)。@ShellCommandGroup
在包裝上放置 (通過 ) 在其中定義命令。這適用于 包(除非在方法或類級別重寫,如前所述)。@ShellCommandGroup
package-info.java
下面的清單顯示了一個示例:
public class UserCommands { @ShellMethod(value = "This command ends up in the "User Commands" group") public void foo() {} @ShellMethod(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() {}}
由于應用程序的內(nèi)部狀態(tài),注冊的命令并不總是有意義的。 例如,可能有一個命令,但它僅在用戶在遙控器上使用后才有效 服務器。現(xiàn)在,如果用戶嘗試使用該命令,shell 應該解釋 該命令存在,但當時不可用。 Spring Shell 可以讓你做到這一點,甚至讓你提供一個簡短的解釋,說明原因 命令不可用。??download?
???connect?
???download?
?
命令有三種可能的方法指示可用性。 它們都使用返回 . 請考慮以下示例:??Availability?
?
@ShellComponentpublic 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"); }}
該方法用于連接到服務器(詳細信息省略),更改狀態(tài) 完成后通過布爾值的命令。 由于存在,在用戶連接之前標記為不可用的命令 與名稱中帶有后綴的命令方法完全相同的方法。 該方法返回 的實例,該實例使用兩個工廠方法之一構造。 如果該命令不可用,則必須提供解釋。 現(xiàn)在,如果用戶嘗試在未連接的情況下調(diào)用該命令,則會發(fā)生以下情況:??connect?
???connected?
???download?
???download?
???Availability?
???Availability?
?
shell:>downloadCommand "download" exists but is not currently available because you are not connected.Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.
有關當前不可用命令的信息也用于集成幫助中。請參閱幫助。
如果將命令不可用時提供的原因附加到“因為”之后,則應很好地閱讀。 您不應以大寫字母開頭句子或添加最后一個句點 |
如果在命令方法的名稱后命名可用性方法不適合您,則 可以使用注釋提供顯式名稱:??@ShellMethodAvailability?
?
@ShellMethod("Download the nuclear codes.") @ShellMethodAvailability("availabilityCheck") public void download() { [...] } public Availability availabilityCheck() { return connected ? Availability.available() : Availability.unavailable("you are not connected"); }
名稱必須匹配 |
最后,通常情況下,同一類中的多個命令共享相同的內(nèi)部狀態(tài),因此, 應作為一個組全部可用或不可用。Spring Shell 不必在所有命令方法上粘貼,而是允許您翻轉(zhuǎn)內(nèi)容并將注釋放在可用性方法上,指定它控制的命令的名稱:??@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"); }
該屬性的缺省值為 。這個特別的 通配符匹配所有命令名稱。這使得打開或關閉單個類的所有命令變得容易 使用單一可用性方法:? @ShellComponentpublic class Toggles { @ShellMethodAvailability public Availability availabilityOnWeekdays() { return Calendar.getInstance().get(DAY_OF_WEEK) == SUNDAY ? Availability.available() : Availability.unavailable("today is not Sunday"); } @ShellMethod public void foo() {} @ShellMethod public void bar() {}} |
Spring Shell 對如何編寫命令和如何組織類沒有施加太多限制。 但是,將相關命令放在同一個類中通常是一種很好的做法,并且可用性指示器 可以從中受益。 |
許多命令行應用程序在適用時返回運行環(huán)境的退出代碼可用于區(qū)分命令是否已成功執(zhí)行。在 這主要與命令在非交互模式下運行時有關,這意味著一個命令 始終使用 .??spring-shell?
???spring-shell?
?
退出代碼的默認行為為:
命令選項解析中的錯誤將導致代碼2
任何一般錯誤將導致結果代碼1
顯然,在任何其他情況下,結果代碼是0
每個都可以在異常和退出代碼之間定義自己的映射。 從本質(zhì)上講,我們綁定到關于退出代碼的功能,并且簡單地 融入其中。??CommandRegistration?
???Spring Boot?
?
假設下面有一個異常顯示,它將從命令中拋出:
static class MyException extends RuntimeException { private final int code; MyException(String msg, int code) { super(msg); this.code = code; } public int getCode() { return code; }}
可以在和退出代碼之間定義映射函數(shù)。你也可以 只需配置一個類即可退出代碼,這只是配置中的語法糖。??Throwable?
?
CommandRegistration.builder() .withExitCode() .map(MyException.class, 3) .map(t -> { if (t instanceof MyException) { return ((MyException) t).getCode(); } return 0; }) .and() .build();
退出代碼不能使用基于注釋的配置進行自定義 |
命令行參數(shù)可以分為選項和位置參數(shù)。 以下各節(jié)介紹如何定義和使用選項的功能。
選項可以在目標方法中定義為方法參數(shù)中的注釋 或以編程方式使用 .??CommandRegistration?
?
具有帶參數(shù)的目標方法會自動注冊到匹配項 參數(shù)名稱。
public String example(String arg1) { return "Hello " + arg1;}
??@ShellOption?
?注釋可用于定義選項名稱,如果 不希望它與參數(shù)名稱相同。
public String example(@ShellOption(value = { "--argx" }) String arg1) { return "Hello " + arg1;}
如果定義選項名稱時不帶前綴,則發(fā)現(xiàn) 或 來自ShellMethod#前綴。??-?
???--?
?
public String example(@ShellOption(value = { "argx" }) String arg1) { return "Hello " + arg1;}
編程方式是使用方法添加一個長名稱。??CommandRegistration?
?
CommandRegistration.builder() .withOption() .longNames("arg1") .and() .build();
大多數(shù)短樣式POSIX選項只是長格式的同義詞,但 添加附加功能以將這些選項組合在一起。有短 選項A、B、C可用作 。??-abc?
?
以編程方式使用短名稱函數(shù)定義短選項。
CommandRegistration.builder() .withOption() .shortNames("a") .and() .withOption() .shortNames("b") .and() .withOption() .shortNames("c") .and() .build();
Short option with combined format is powerful if type is defined as a flag which means type is aboolean. That way you can define a presense of a flags as,or.??-abc?
???-abc true?
???-abc false?
?
CommandRegistration.builder() .withOption() .shortNames("a") .type(boolean.class) .and() .withOption() .shortNames("b") .type(boolean.class) .and() .withOption() .shortNames("c") .type(boolean.class) .and() .build();
使用注釋模型,您可以直接定義短參數(shù)。
public String example( @ShellOption(value = { "-a" }) String arg1, @ShellOption(value = { "-b" }) String arg2, @ShellOption(value = { "-c" }) String arg3) { return "Hello " + arg1;}
有時,您希望使用選項更精細地控制參數(shù)的數(shù)量 在發(fā)生分析操作時進行處理。Arity 定義為最小值和最大值 值,其中 min 必須是正整數(shù),max 必須大于或等于 min。
CommandRegistration.builder() .withOption() .longNames("arg1") .arity(0, 1) .and() .build();
Arity 也可以定義為枚舉,它們是快捷方式 在下表中:??OptionArity?
?
CommandRegistration.builder() .withOption() .longNames("arg1") .arity(OptionArity.EXACTLY_ONE) .and() .build();
Table 1. OptionArity
價值 | 最小/最大 |
零 | 0 / 0 |
ZERO_OR_ONE | 0 / 1 |
EXACTLY_ONE | 1 / 1 |
ZERO_OR_MORE | 0 / 整數(shù)最大值 |
ONE_OR_MORE | 1 / 整數(shù)最大值 |
注釋模型僅支持定義 arity 的最大值。
public String example(@ShellOption(arity = 1) String arg1) { return "Hello " + arg1;}
位置信息主要與命令目標方法相關:
CommandRegistration.builder() .withOption() .longNames("arg1") .position(0) .and() .build();
An option is either required or not and, generally speaking, how it behaves depends on a command target:
CommandRegistration.builder() .withOption() .longNames("arg1") .required() .and() .build();
In the annotation model, there is no direct way to define if argument is optional. Instead, it is instructed to be:??NULL?
?
public String example( @ShellOption(defaultValue = ShellOption.NULL) String arg1) { return "Hello " + arg1;}
Having a default value for an option is somewhat related toOptional Value, as there are cases where you may want to know if the user defined an option and change behavior based on a default value:
CommandRegistration.builder() .withOption() .longNames("arg1") .defaultValue("defaultValue") .and() .build();
注釋模型還支持定義缺省值:
public String example( @ShellOption(defaultValue = "defaultValue") String arg1) { return "Hello " + arg1;}
Spring Shell 與Bean Validation API集成以支持 對命令參數(shù)的自動和自記錄約束。
在命令參數(shù)上找到的注釋和方法級別的注釋是 在執(zhí)行命令之前遵循并觸發(fā)驗證。請考慮以下命令:
@ShellMethod("Change password.") public String changePassword(@Size(min = 8, max = 40) String password) { return "Password successfully set to " + password; }
從前面的示例中,您可以免費獲得以下行為:
shell:>change-password helloThe following constraints were not met: --password string : size must be between 8 and 40 (You passed "hello")
選項標簽在 shell 本身中沒有功能行為,除了 默認命令輸出的內(nèi)容。在命令文檔中 記錄了一種類型的選項,但這并不總是非常有用。因此 您可能希望為選項提供更好的描述性詞。??help?
?
CommandRegistration.builder() .withOption() .longNames("arg1") .and() .withOption() .longNames("arg2") .label("MYLABEL") .and() .build();
定義標簽隨后顯示在 中。??help?
?
my-shell:>help mycommandNAME mycommand -SYNOPSIS mycommand --arg1 String --arg2 MYLABELOPTIONS --arg1 String [Optional] --arg2 MYLABEL [Optional]
Spring Shell 可以為兩個交互式 shell 提供完成建議 和命令行。但是,當外殼在 交互模式我們有一個 shell 的活動實例,這意味著它是 更容易提供更多編程的方式來提供完成提示。 當 shell 純粹作為命令行工具運行時,完成只能 通過集成到操作系統(tǒng)級別的shell中完成,例如bash。
完成提示是使用函數(shù)或界面樣式計算的 獲取并返回實例列表的方法。 給你各種 有關當前上下文(如命令注冊和選項)的信息。??CompletionContext?
???CompletionProposal?
???CompletionContext?
?
通用解析器可以注冊為 bean(如果它們有用) 對于所有命令和場景。例如現(xiàn)有完成 實現(xiàn)處理完成 作為選項名稱。? |
static class MyValuesCompletionResolver implements CompletionResolver { @Override public Listapply(CompletionContext t) { return Arrays.asList("val1", "val2").stream() .map(CompletionProposal::new) .collect(Collectors.toList()); }}
具有基于生成器的命令注冊的選項值可以是 按選項定義。
void dump1() { CommandRegistration.builder() .withOption() .longNames("arg1") .completion(ctx -> { return Arrays.asList("val1", "val2").stream() .map(CompletionProposal::new) .collect(Collectors.toList()); }) .and() .build();}
處理具有基于注釋的命令注冊的選項值 通過可以通過注釋定義的接口。??ValueProvider?
???@ShellOption?
?
static class MyValuesProvider implements ValueProvider { @Override public Listcomplete(CompletionContext completionContext) { return Arrays.asList("val1", "val2").stream() .map(CompletionProposal::new) .collect(Collectors.toList()); }}
實際使用基于注釋的命令需要 注冊為豆子。??ValueProvider?
?
@ShellMethod(value = "complete", key = "complete")public String complete( @ShellOption(valueProvider = MyValuesProvider.class) String arg1){ return "You said " + arg1;}
命令行完成目前僅支持bash并已記錄在案 在內(nèi)置命令完成中。??completion?
?
本節(jié)介紹如何構建 Spring Shell 應用程序。
版本 2.1.x 包括對編譯 Spring Shell 應用程序的實驗性支持 使用GraalVM和Spring Native進入本機應用程序。因為底層的JLine 庫適用于 GraalVM,大多數(shù)事情都應該正常工作。
您可以使用本機配置文件編譯項目以獲取本機應用程序:
$ ./mvnw clean package -Pnative
然后,您可以在交互或非交互模式下運行應用程序:
$ ./spring-shell-samples/target/spring-shell-samples helpAVAILABLE COMMANDSBuilt-In Commands completion bash: Generate bash completion script help: Display help about available commands. history: Display or save the history of previously run commands script: Read and execute commands from a file....
組件是一組內(nèi)置功能或其他功能 您可以根據(jù)自己的需要重復使用或擴展。有問題的組件是 內(nèi)置命令或提供更高級別 UI 端組件 命令本身中的功能。
運行 shell 應用程序通常意味著用戶處于圖形受限的狀態(tài) 環(huán)境。此外,雖然在手機時代我們幾乎總是連接, 訪問 Web 瀏覽器或任何其他富 UI 應用程序(如 PDF 查看器)可能并不總是 是可能的。這就是為什么正確自我記錄 shell 命令很重要的原因,這就是命令的用武之地。??help?
?
鍵入 + 列出 shell 已知的所有命令(包括不可用的命令) 以及對他們所做的事情的簡短描述,類似于以下內(nèi)容:??help?
???ENTER?
?
my-shell:>helpAVAILABLE COMMANDSBuilt-In Commands exit: Exit the shell. help: Display help about available commands stacktrace: Display the full stacktrace of the last error. clear: Clear the shell screen. quit: Exit the shell. history: Display or save the history of previously run commands completion bash: Generate bash completion script version: Show version info script: Read and execute commands from a file.
鍵入顯示有關命令的更多詳細信息,包括可用參數(shù)及其 類型,它們是否是強制性的,以及其他詳細信息。??help
?
以下清單顯示了應用于自身的命令:??help?
?
my-shell:>help helpNAME help - Display help about available commandsSYNOPSIS help --command StringOPTIONS --command or -C String The command to obtain help for. [Optional]
幫助是模板化的,可以根據(jù)需要進行自定義。設置位于可用于禁用命令、執(zhí)行或是否要通過平展隱藏組的位置 結構, 用于定義用于輸出命令幫助的模板,用于定義 命令列表的輸出。??spring.shell.command.help?
???enabled?
???grouping-mode?
???group?
???flat?
???command-template?
???commands-template?
?
如果已設置,則幫助將顯示:??spring.shell.command.help.grouping-mode=flat?
?
my-shell:>help helpAVAILABLE COMMANDSexit: Exit the shell.help: Display help about available commandsstacktrace: Display the full stacktrace of the last error.clear: Clear the shell screen.quit: Exit the shell.history: Display or save the history of previously run commandscompletion bash: Generate bash completion scriptversion: Show version infoscript: Read and execute commands from a file.
輸出自 和 都使用默認實現(xiàn)進行模板化 可以更改。??help?
???help
?
選項默認為模型并作為模型傳遞。??spring.shell.command.help.commands-template?
???classpath:template/help-commands-default.stg?
???GroupsInfoModel?
?
選項默認為模型并作為模型傳遞。??spring.shell.command.help.command-template?
???classpath:template/help-command-default.stg?
???CommandInfoModel?
?
Table 2. GroupsInfoModel Variables
鑰匙 | 描述 |
? | ? |
? | 命令變量(請參閱組命令信息模型變量)。 |
? | 命令變量(請參閱命令信息模型變量)。 |
? | ? |
Table 3. GroupCommandInfoModel Variables
鑰匙 | 描述 |
? | 組的名稱(如果已設置)。否則為空。 |
? | 命令(如果已設置)。否則為空。類型是一個多值,請參閱命令信息模型變量。 |
Table 4. CommandInfoModel Variables
鑰匙 | 描述 |
? | 命令的名稱(如果已設置)。否則為空。類型為字符串,包含完整命令。 |
? | 命令的名稱(如果已設置)。否則為空。類型是多值本質(zhì)上是拆分的。? |
? | 可能的別名(如果已設置)。類型是帶有字符串的多值。 |
? | 命令的說明(如果已設置)。否則為空。 |
? | 參數(shù)變量(如果已設置)。否則為空。類型是一個多值,請參閱命令參數(shù)信息模型變量。 |
? | 可用性變量(請參閱??命令可用性信息模型變量??)。 |
Table 5. CommandParameterInfoModel Variables
鑰匙 | 描述 |
? | 參數(shù)的類型(如果已設置)。否則為空。 |
? | 參數(shù)(如果已設置)。否則為空。類型是帶有字符串的多值。 |
? | ? |
? | 參數(shù)的說明(如果已設置)。否則為空。 |
? | 參數(shù)的默認值(如果已設置)。否則為空。 |
? | ? |
Table 6. CommandAvailabilityInfoModel Variables
鑰匙 | 描述 |
? | ? |
? | 如果設置,則不可用的原因。否則為空。 |
該命令執(zhí)行您的預期操作并清除屏幕,重置提示 在左上角。??clear?
?
該命令(也稱為 )請求 shell 正常退出 關閉 Spring 應用程序上下文。如果不被覆蓋,JLine Bean 會寫入所有 命令,以便在下次啟動時再次可用。??quit?
???exit?
???History?
?
當命令代碼中發(fā)生異常時,shell 會捕獲該異常,并顯示一條簡單的單行消息 以免用戶溢出太多信息。 但是,在某些情況下,了解究竟發(fā)生了什么很重要(特別是如果異常具有嵌套原因)。
為此,Spring Shell 會記住上次發(fā)生的異常,用戶稍后可以使用該命令在控制臺上打印所有詳細信息。??stacktrace?
?
該命令接受本地文件作為參數(shù),并重播在那里找到的命令,一次一個。??script?
?
從文件中讀取的行為與交互式 shell 內(nèi)部的行為完全相同,因此考慮以 開頭的行 為注釋并被忽略,而以觸發(fā)行繼續(xù)結尾的行。??//?
???\?
?
該命令顯示已執(zhí)行命令的歷史記錄。??history?
?
有幾個配置選項可用于配置行為 的歷史。歷史記錄保存在日志文件中,默認情況下啟用該文件,并且可以 通過設置 關閉。日志文件的名稱 解析自 并默認為 , 您可以通過設置 .??spring.shell.history.enabled?
???spring.application.name?
???spring-shell.log?
???spring.shell.history.name?
?
默認情況下,日志文件會生成到當前工作目錄,您可以指示 通過設置 .此屬性可以包含 一個占位符 (),它解析為公共共享配置目錄。??spring.shell.config.location?
???{userconfig}?
?
運行 Spring Shell 應用程序以查看示例應用程序在使用這些選項時的工作方式。 |
該命令集允許您創(chuàng)建可以使用的腳本文件 與 am OS shell 實現(xiàn)一起提供完成。這在以下情況下非常有用 使用非交互模式。??completion?
?
目前,唯一的實現(xiàn)是 bash,它與 sub-command 一起工作。??bash?
?
該命令通過集成到 引導的,如果這些存在于 shell 應用程序中。 默認情況下只顯示版本信息,可以通過配置開啟其他信息 選項。??version?
???BuildProperties?
???GitProperties?
?
相關設置位于 下,您可以在其中使用 禁用命令,并可以選擇使用 定義您自己的模板。您可以使用 、、、、、、 和 命令來控制 默認模板中的字段。??spring.shell.command.version?
???enabled?
???template?
???show-build-artifact?
???show-build-group?
???show-build-name?
???show-build-time?
???show-build-version?
???show-git-branch?
???show-git-commit-id?
???show-git-short-commit-id?
???show-git-commit-time?
?
模板默認為 ,您可以定義 您自己的,如以下示例所示:??classpath:template/version-default.st?
?
此設置將輸出如下所示的內(nèi)容:
X.X.X
可以將以下屬性添加到默認模板呈現(xiàn)中:、、 和 。??buildVersion?
???buildGroup?
???buildGroup?
???buildName?
???buildTime?
???gitShortCommitId?
???gitCommitId?
???gitBranch?
???gitCommitTime?
?
當您使用流組件構建涉及的內(nèi)容時 使用多個組件,您的實現(xiàn)可能會變得有點混亂。 為了簡化這些用例,我們添加了一個可以將多個組件執(zhí)行掛接在一起的 作為“流”。??ComponentFlow?
?
以下清單顯示了 shell 中的流及其輸出的示例:
static class FlowSampleComplex { @Autowired private ComponentFlow.Builder componentFlowBuilder; public void runFlow() { Mapsingle1SelectItems = new HashMap<>(); single1SelectItems.put("key1", "value1"); single1SelectItems.put("key2", "value2"); List multi1SelectItems = Arrays.asList(SelectItem.of("key1", "value1"), SelectItem.of("key2", "value2"), SelectItem.of("key3", "value3")); ComponentFlow flow = componentFlowBuilder.clone().reset() .withStringInput("field1") .name("Field1") .defaultValue("defaultField1Value") .and() .withStringInput("field2") .name("Field2") .and() .withConfirmationInput("confirmation1") .name("Confirmation1") .and() .withPathInput("path1") .name("Path1") .and() .withSingleItemSelector("single1") .name("Single1") .selectItems(single1SelectItems) .and() .withMultiItemSelector("multi1") .name("Multi1") .selectItems(multi1SelectItems) .and() .build(); flow.run(); }}
組件的正常執(zhí)行順序與使用生成器定義的順序相同。它 可以使用函數(shù)并返回目標組件 ID有條件地選擇在流中跳轉(zhuǎn)的位置。如果此返回的 id 為aither null或不存在,則流基本上會在那里停止。??next?
?
static class FlowSampleConditional { @Autowired private ComponentFlow.Builder componentFlowBuilder; public void runFlow() { Mapsingle1SelectItems = new HashMap<>(); single1SelectItems.put("Field1", "field1"); single1SelectItems.put("Field2", "field2"); ComponentFlow flow = componentFlowBuilder.clone().reset() .withSingleItemSelector("single1") .name("Single1") .selectItems(single1SelectItems) .next(ctx -> ctx.getResultItem().get().getItem()) .and() .withStringInput("field1") .name("Field1") .defaultValue("defaultField1Value") .next(ctx -> null) .and() .withStringInput("field2") .name("Field2") .defaultValue("defaultField2Value") .next(ctx -> null) .and() .build(); flow.run(); }}
運行流的結果返回 ,您可以 用于執(zhí)行進一步的操作。? |
從版本 2.1.x 開始,新的組件模型提供了 為通常用例創(chuàng)建更高級別的用戶交互的更簡單方法, 例如要求以各種形式輸入。這些通常只是純文本 輸入或從列表中選擇內(nèi)容。
內(nèi)置組件的模板位于類路徑中。??org/springframework/shell/component?
?
內(nèi)置組件通常遵循以下邏輯:
輸入用戶輸入的運行循環(huán)。生成與組件相關的上下文。呈現(xiàn)組件狀態(tài)的運行時狀態(tài)。退出。呈現(xiàn)組件狀態(tài)的最終狀態(tài)。流為定義 更適合定義交互式命令流的組件。 |
您可以通過以下兩種方式之一實現(xiàn)組件渲染: 以編程方式或使用ANTLR 字符串模板。 嚴格來說,有一個簡單的渲染器接口 作為輸入并輸出 . 這使您可以在模板和代碼之間進行選擇。??Function?
???Context?
???AttributedString?
?
模板是一個不錯的選擇,如果你不需要做任何復雜的事情或 您只想稍微修改現(xiàn)有組件布局。渲染 然后,通過代碼,您可以靈活地執(zhí)行所需的任何操作。
以編程方式呈現(xiàn)是創(chuàng)建一個:??Function?
?
class StringInputCustomRenderer implements Function> { @Override public List apply(StringInputContext context) { AttributedStringBuilder builder = new AttributedStringBuilder(); builder.append(context.getName()); builder.append(" "); if (context.getResultValue() != null) { builder.append(context.getResultValue()); } else { String input = context.getInput(); if (StringUtils.hasText(input)) { builder.append(input); } else { builder.append("[Default " + context.getDefaultValue() + "]"); } } return Arrays.asList(builder.toAttributedString()); }}
然后,您可以將其掛接到組件:
@ShellMethod(key = "component stringcustom", value = "String input", group = "Components")public String stringInputCustom(boolean mask) { StringInput component = new StringInput(getTerminal(), "Enter value", "myvalue", new StringInputCustomRenderer()); component.setResourceLoader(getResourceLoader()); component.setTemplateExecutor(getTemplateExecutor()); if (mask) { component.setMaskCharater("*"); } StringInputContext context = component.run(StringInputContext.empty()); return "Got value " + context.getResultValue();}
組件有自己的上下文,但通常共享一些功能 從父組件類型。下表顯示了這些上下文變量:
Table 7. TextComponentContext Template Variables
鑰匙 | 描述 |
? | 組件呈現(xiàn)其結果后的值。 |
? | 組件的名稱,即其標題。 |
? | 組件的可能消息集。 |
? | 消息的級別 — 、 或 之一。? |
? | 如果級別為 。否則,為假。? |
? | 如果級別為 。否則,為假。? |
? | 如果級別為 。否則,為假。? |
? | 原始用戶輸入。 |
Table 8. SelectorComponentContext Template Variables
鑰匙 | 描述 |
? | 組件的名稱,即其標題。 |
? | 原始用戶輸入 — 主要用于篩選。 |
? | 項目狀態(tài)的完整列表。 |
? | 項目狀態(tài)的可見列表。 |
? | 如果上下文處于結果模式,則返回。? |
? | 選擇器中的當前游標行。 |
字符串輸入組件要求用戶輸入簡單的文本,可以選擇屏蔽值 如果內(nèi)容包含敏感內(nèi)容。下面的清單顯示了一個示例:
@ShellComponentpublic class ComponentCommands extends AbstractShellComponent { @ShellMethod(key = "component string", value = "String input", group = "Components") public String stringInput(boolean mask) { StringInput component = new StringInput(getTerminal(), "Enter value", "myvalue"); component.setResourceLoader(getResourceLoader()); component.setTemplateExecutor(getTemplateExecutor()); if (mask) { component.setMaskCharater("*"); } StringInputContext context = component.run(StringInputContext.empty()); return "Got value " + context.getResultValue(); }}
下圖顯示了字符串輸入組件的典型輸出:
上下文對象為 。下表列出了其上下文變量:??StringInputContext?
?
Table 9. StringInputContext Template Variables
鑰匙 | 描述 |
? | 默認值(如果已設置)。否則為空。 |
? | 屏蔽的輸入值 |
? | 屏蔽結果值 |
? | 掩碼字符(如果已設置)。否則為空。 |
? | ? |
? | 父上下文變量(請參閱文本組件上下文模板變量)。 |
路徑輸入組件要求用戶提供 a 并提供有關路徑本身的其他信息。??Path?
?
@ShellComponentpublic class ComponentCommands extends AbstractShellComponent { @ShellMethod(key = "component path", value = "Path input", group = "Components") public String pathInput() { PathInput component = new PathInput(getTerminal(), "Enter value"); component.setResourceLoader(getResourceLoader()); component.setTemplateExecutor(getTemplateExecutor()); PathInputContext context = component.run(PathInputContext.empty()); return "Got value " + context.getResultValue(); }}
下圖顯示了路徑輸入組件的典型輸出:
上下文對象為 。下表描述了其上下文變量:??PathInputContext?
?
Table 10. PathInputContext Template Variables
鑰匙 | 描述 |
? | 父上下文變量(請參閱文本組件上下文模板變量)。 |
確認組件要求用戶進行簡單的確認。它本質(zhì)上是一個 是或否問題。
@ShellComponentpublic class ComponentCommands extends AbstractShellComponent { @ShellMethod(key = "component confirmation", value = "Confirmation input", group = "Components") public String confirmationInput(boolean no) { ConfirmationInput component = new ConfirmationInput(getTerminal(), "Enter value", !no); component.setResourceLoader(getResourceLoader()); component.setTemplateExecutor(getTemplateExecutor()); ConfirmationInputContext context = component.run(ConfirmationInputContext.empty()); return "Got value " + context.getResultValue(); }}
下圖顯示了確認組件的典型輸出:
上下文對象為 。下表描述了其上下文變量:??ConfirmationInputContext?
?
Table 11. ConfirmationInputContext Template Variables
鑰匙 | 描述 |
? | 默認值 — 或 。? |
? | 父上下文變量(請參閱文本組件上下文模板變量)。 |
單個選擇組件要求用戶從列表中選擇一個項目。它類似于一個簡單的 保管箱實施。下面的清單顯示了一個示例:
@ShellComponentpublic class ComponentCommands extends AbstractShellComponent { @ShellMethod(key = "component single", value = "Single selector", group = "Components") public String singleSelector() { SelectorItemi1 = SelectorItem.of("key1", "value1"); SelectorItem i2 = SelectorItem.of("key2", "value2"); List > items = Arrays.asList(i1, i2); SingleItemSelector > component = new SingleItemSelector<>(getTerminal(), items, "testSimple", null); component.setResourceLoader(getResourceLoader()); component.setTemplateExecutor(getTemplateExecutor()); SingleItemSelectorContext > context = component .run(SingleItemSelectorContext.empty()); String result = context.getResultItem().flatMap(si -> Optional.ofNullable(si.getItem())).get(); return "Got value " + result; }}
下圖顯示了單個選擇組件的典型輸出:
上下文對象為 。下表描述了其上下文變量:??SingleItemSelectorContext?
?
Table 12. SingleItemSelectorContext Template Variables
鑰匙 | 描述 |
? | 組件存在時的返回值。 |
? | 可見項目,其中行包含名稱和選定項目的映射。 |
? | 父上下文變量(請參閱選擇器組件上下文模板變量)。 |
您可以通過定義要公開的項目來預先選擇該項目。這是 如果您知道默認值并允許用戶僅按下即可做出選擇,則很有用。 以下清單設置默認值:??Enter?
?
SelectorItemi1 = SelectorItem.of("key1", "value1");SelectorItem i2 = SelectorItem.of("key2", "value2");List > items = Arrays.asList(i1, i2);SingleItemSelector > component = new SingleItemSelector<>(getTerminal(), items, "testSimple", null);component.setDefaultExpose(i2);
多選組件要求用戶從列表中選擇多個項目。 下面的清單顯示了一個示例:
@ShellComponentpublic class ComponentCommands extends AbstractShellComponent { @ShellMethod(key = "component multi", value = "Multi selector", group = "Components") public String multiSelector() { List> items = new ArrayList<>(); items.add(SelectorItem.of("key1", "value1")); items.add(SelectorItem.of("key2", "value2", false, true)); items.add(SelectorItem.of("key3", "value3")); MultiItemSelector > component = new MultiItemSelector<>(getTerminal(), items, "testSimple", null); component.setResourceLoader(getResourceLoader()); component.setTemplateExecutor(getTemplateExecutor()); MultiItemSelectorContext > context = component .run(MultiItemSelectorContext.empty()); String result = context.getResultItems().stream() .map(si -> si.getItem()) .collect(Collectors.joining(",")); return "Got value " + result; }}
下圖顯示了一個典型的多選組件:
上下文對象為 。下表描述了其上下文變量:??MultiItemSelectorContext?
?
Table 13. MultiItemSelectorContext Template Variables
鑰匙 | 描述 |
? | 組件存在時返回的值。 |
? | 可見項目,其中行包含名稱、選定、行內(nèi)和啟用項目的映射。 |
? | 父上下文變量(請參閱選擇器組件上下文模板變量)。 |
本節(jié)介紹如何自定義外殼。
當前的終端實現(xiàn)功能豐富,通常可以顯示 其他只是純文本的東西。例如,文本的樣式可以設置為粗體或具有不同的顏色。終端能夠 顯示 Unicode 表中的各種字符,例如表情符號,通常是 用于使外殼輸出更漂亮。
Spring Shell 通過它的主題框架支持這些,該框架包含兩個部分, 首先樣式可用于更改文本類型,其次是圖形如何 顯示一些字符。然后將這兩者組合在一起作為一個主題。
有關主題化內(nèi)部的更多信息,請參閱主題化。
默認主題已命名,但可以使用屬性進行更改。其他名為用途的內(nèi)置主題 沒有顏色樣式,盡量不使用任何特殊圖形。? |
通過覆蓋設置來修改現(xiàn)有樣式。
static class MyStyleSettings extends StyleSettings { @Override public String highlight() { return super.highlight(); }}
通過覆蓋設置來修改現(xiàn)有圖形。
static class MyFigureSettings extends FigureSettings { @Override public String error() { return super.error(); }}
要創(chuàng)建新主題,請創(chuàng)建并提供您自己的樣式和圖形實現(xiàn)。??ThemeSettings?
?
static class MyThemeSettings extends ThemeSettings { @Override public StyleSettings styles() { return new MyStyleSettings(); } @Override public FigureSettings figures() { return new MyFigureSettings(); }}
注冊一個新 Bean,您可以在其中返回自定義和主題名稱。??Theme?
???ThemeSettings?
?
@Configurationstatic class CustomThemeConfig { @Bean Theme myTheme() { return new Theme() { @Override public String getName() { return "mytheme"; } @Override public ThemeSettings getSettings() { return new MyThemeSettings(); } }; }}
如果要創(chuàng)建,可以使用 來解析樣式JLine 樣式的字符串以編程方式和數(shù)字(如果需要) 主題人物更漂亮。??ThemeResolver?
?
@Autowiredprivate ThemeResolver resolver;void resolve() { String resolvedStyle = resolver.resolveStyleTag(StyleSettings.TAG_TITLE); // bold,fg:bright-white AttributedStyle style = resolver.resolveStyle(resolvedStyle); // jline attributed style from expression above String resolvedFigure = resolver.resolveFigureTag(FigureSettings.TAG_ERROR); // character i.e. U+2716 Heavy Multiplication X Emoji, cross}