溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何通過Spring Shell開發(fā)Java命令行應(yīng)用

發(fā)布時(shí)間:2021-07-16 13:39:26 來源:億速云 閱讀:525 作者:小新 欄目:開發(fā)技術(shù)

小編給大家分享一下如何通過Spring Shell開發(fā)Java命令行應(yīng)用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

Spring Shell 入門

最簡(jiǎn)單的創(chuàng)建 Spring Shell 應(yīng)用的方式是使用 Spring Boot。從 Spring Initializr 網(wǎng)站(http://start.spring.io/)上創(chuàng)建一個(gè)新的基于 Apache Maven 的 Spring Boot 應(yīng)用,然后添加 Spring Shell 相關(guān)的依賴即可。本文介紹的是 Spring Shell 2.0.0.M2 版本,目前還只是 Milestone 版本,因此需要在 pom.xml 中添加 Spring 提供的包含 Milestone 版本工件的 Maven 倉庫,如代碼清單 1 所示。否則的話,Maven 會(huì)無法找到相應(yīng)的工件。

清單 1. 添加 Spring Shell 的 Maven 倉庫

<repositories>
 <repository>
 <id>spring-milestone</id>
 <name>Spring Repository</name>
 <url>https://repo.spring.io/milestone</url>
 </repository>
</repositories>

在添加了 Spring Shell 的 Maven 倉庫之后,可以在 Spring Boot 項(xiàng)目中添加對(duì)于spring-shell-starter 的依賴,如代碼清單 2 所示。

清單 2. 添加 Spring Shell 所需 Maven 依賴

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

我們接著可以創(chuàng)建第一個(gè)基于 Spring Shell 的命令行應(yīng)用。該應(yīng)用根據(jù)輸入的參數(shù)來輸出相應(yīng)的問候語,完整的代碼如清單 3 所示。從代碼清單 3 中可以看到,在 Spring Shell 的幫助下,完整的實(shí)現(xiàn)代碼非常簡(jiǎn)單。代碼的核心是兩個(gè)注解:@ShellComponent 聲明類GreetingApp 是一個(gè) Spring Shell 的組件;@ShellMethod 表示方法 sayHi 是可以在命令行運(yùn)行的命令。該方法的參數(shù) name 是命令行的輸入?yún)?shù),而其返回值是命令行執(zhí)行的結(jié)果。

清單 3. 輸出問候語的命令行應(yīng)用

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

接下來我們運(yùn)行該應(yīng)用。運(yùn)行起來之后,該應(yīng)用直接進(jìn)入命令行提示界面,我們可以輸入 help 來輸出使用幫助。help 是 Spring Shell 提供的眾多內(nèi)置命令之一,在列出的命令中,可以看到我們創(chuàng)建的 say-hi 命令。我們輸入"say-hi Alex"來運(yùn)行該命令,可以看到輸出的結(jié)果"Hi Alex"。如果我們直接輸入"say-hi",會(huì)看到輸出的錯(cuò)誤信息,告訴我們參數(shù)"--name"是必須的。從上面的例子可以看出,在 Spring Shell 的幫助下,創(chuàng)建一個(gè)命令行應(yīng)用是非常簡(jiǎn)單的。很多實(shí)用功能都已經(jīng)默認(rèn)提供了。在使用 Spring Initializr 創(chuàng)建的 Spring Boot 項(xiàng)目中,默認(rèn)提供了一個(gè)單元測(cè)試用例。這個(gè)默認(rèn)的單元測(cè)試用例與 Spring Shell 在使用時(shí)存在沖突。在進(jìn)行代碼清單 3 中的項(xiàng)目的 Maven 構(gòu)建時(shí),該測(cè)試用例需要被禁用,否則構(gòu)建過程會(huì)卡住。

參數(shù)傳遞與校驗(yàn)

下面我們討論 Spring Shell 中的參數(shù)傳遞和校驗(yàn)。Spring Shell 支持兩種不同類型的參數(shù),分別是命名參數(shù)和位置參數(shù)。命名參數(shù)有名稱,可以通過類似--arg 的方式來指定;位置參數(shù)則按照其在方法的參數(shù)列表中的出現(xiàn)位置來進(jìn)行匹配。命名參數(shù)和位置參數(shù)可以混合起來使用,不過命名參數(shù)的優(yōu)先級(jí)更高,會(huì)首先匹配命名參數(shù)。每個(gè)參數(shù)都有默認(rèn)的名稱,與方法中的對(duì)應(yīng)的參數(shù)名稱一致。

在代碼清單 4 中的方法有 3 個(gè)參數(shù) a、b 和 c。在調(diào)用該命令時(shí),可以使用"echo1 --a 1 --b 2 --c 3",也可以使用"echo1 --a 1 2 3"或"echo1 1 3 --b 2"。其效果都是分別把 1,2 和 3 賦值給 a、b 和 c。

清單 4. 包含多個(gè)參數(shù)的命令方法

@ShellMethod("Echo1")
public String echo1(int a, int b, int c) {
 return String.format("a = %d, b = %d, c = %d", a, b, c);
}

如果不希望使用方法的參數(shù)名稱作為命令對(duì)應(yīng)參數(shù)的名稱,可以通過@ShellOption 來標(biāo)注所要使用的一個(gè)或多個(gè)參數(shù)名稱。我們可以通過指定多個(gè)參數(shù)名稱來提供不同的別名。在代碼清單 5 中,為參數(shù) b 指定了一個(gè)名稱 boy。可以通過"echo2 1 --boy 2 3"來調(diào)用。

清單 5. 指定參數(shù)名稱

@ShellMethod("Echo2")
public String echo2(int a, @ShellOption("--boy") int b, int c) {
 return String.format("a = %d, b = %d, c = %d", a, b, c);
}

對(duì)于命名參數(shù),默認(rèn)使用的是"--"作為前綴,可以通過@ShellMethod 的屬性 prefix 來設(shè)置不同的前綴。方法對(duì)應(yīng)的命令的名稱默認(rèn)是從方法名稱自動(dòng)得到的,可以通過屬性 key 來設(shè)置不同的名稱,屬性 value 表示的是命令的描述信息。如果參數(shù)是可選的,可以通過@ShellOption 的屬性 defaultValue 來設(shè)置默認(rèn)值。在代碼清單 6 中,我們?yōu)榉椒?withDefault 指定了一個(gè)命令名稱 default,同時(shí)為參數(shù) value 指定了默認(rèn)值"Hello"。如果直接運(yùn)行命令"default",輸出的結(jié)果是"Value: Hello";如果運(yùn)行命令"default 123",則輸出的結(jié)果是"Value: 123"。

清單 6. 指定方法名稱和參數(shù)默認(rèn)值

@ShellComponent
public class NameAndDefaultValueApp {
 @ShellMethod(key = "default", value = "With default value")
 public void withDefault(@ShellOption(defaultValue = "Hello") final String value) {
 System.out.printf("Value: %s%n", value);
 }
}

一個(gè)參數(shù)可以對(duì)應(yīng)多個(gè)值。通過@ShellOption 屬性 arity 可以指定一個(gè)參數(shù)所對(duì)應(yīng)的值的數(shù)量。這些參數(shù)會(huì)被添加到一個(gè)數(shù)組中,可以在方法中訪問。在代碼清單 7 中,方法 echo3 的參數(shù) numbers 的 arity 值是 3,因此可以映射 3 個(gè)參數(shù)。在運(yùn)行命令"echo3 1 2 3"時(shí),輸出的結(jié)果是"a = 1, b =2, c = 3"。

清單 7. 參數(shù)對(duì)應(yīng)多個(gè)值

@ShellMethod("Echo3")
public String echo3(@ShellOption(arity = 3) int[] numbers) {
 return String.format("a = %d, b = %d, c = %d", numbers[0], numbers[1], numbers[2]);
}

如果參數(shù)的類型是布爾類型 Boolean,在調(diào)用的時(shí)候不需要給出對(duì)應(yīng)的值。當(dāng)參數(shù)出現(xiàn)時(shí)就表示值為 true。

Spring Shell 支持對(duì)參數(shù)的值使用 Bean Validation API 進(jìn)行驗(yàn)證。比如我們可以用@Size 來限制字符串的長(zhǎng)度,用@Min 和@Max 來限制數(shù)值的大小,如代碼清單 8 所示。

清單 8. 校驗(yàn)參數(shù)

@ShellComponent
public class ParametersValidationApp {
 @ShellMethod("String size")
 public String stringSize(@Size(min = 3, max = 16) String name) {
 return String.format("Your name is %s", name);
 }
 @ShellMethod("Number range")
 public String numberRange(@Min(10) @Max(100) int number) {
 return String.format("The number is %s", number);
 }
}

結(jié)果處理

Spring Shell 在運(yùn)行時(shí),內(nèi)部有一個(gè)處理循環(huán)。在每個(gè)循環(huán)的執(zhí)行過程中,首先讀取用戶的輸入,然后進(jìn)行相應(yīng)的處理,最后再把處理的結(jié)果輸出。這其中的結(jié)果處理是由 org.springframework.shell.ResultHandler 接口來實(shí)現(xiàn)的。Spring Shell 中內(nèi)置提供了對(duì)于不同類型結(jié)果的處理實(shí)現(xiàn)。命令執(zhí)行的結(jié)果可能有很多種:如果用戶輸入的參數(shù)錯(cuò)誤,輸出的結(jié)果應(yīng)該是相應(yīng)的提示信息;如果在命令的執(zhí)行過程中出現(xiàn)了錯(cuò)誤,則需要輸出相應(yīng)的錯(cuò)誤信息;用戶也可能直接退出命令行。Spring Shell 默認(rèn)使用的處理實(shí)現(xiàn)是類 org.springframework.shell.result.IterableResultHandler。IterableResultHandler 負(fù)責(zé)處理 Iterable 類型的結(jié)果對(duì)象。對(duì)于 Iterable 中包含的每個(gè)對(duì)象,把實(shí)際的處理請(qǐng)求代理給另外一個(gè) ResultHandler 來完成。IterableResultHandler 默認(rèn)的代理實(shí)現(xiàn)是類 org.springframework.shell.result.TypeHierarchyResultHandler。TypeHierarchyResultHandler 其實(shí)是一個(gè)復(fù)合的處理器,它會(huì)把對(duì)于不同類型結(jié)果的 ResultHandler 接口的實(shí)現(xiàn)進(jìn)行注冊(cè),然后根據(jù)結(jié)果的類型來選擇相應(yīng)的處理器實(shí)現(xiàn)。如果找不到類型完全匹配的處理器實(shí)現(xiàn),則會(huì)沿著結(jié)果類型的層次結(jié)構(gòu)樹往上查找,直到找到對(duì)應(yīng)的處理器實(shí)現(xiàn)。Spring Shell 提供了對(duì)于 Object 類型結(jié)果的處理實(shí)現(xiàn)類 org.springframework.shell.result.DefaultResultHandler,因此所有的結(jié)果類型都可以得到處理。DefaultResultHandler 所做的處理只是把 Object 類型轉(zhuǎn)換成 String,然后輸出到控制臺(tái)。

了解了 Spring Shell 對(duì)于結(jié)果的處理方式之后,我們可以添加自己所需要的特定結(jié)果類型的處理實(shí)現(xiàn)。代碼清單 9 給了一個(gè)作為示例的處理結(jié)果類 PrefixedResult。PrefixedResult 中包含一個(gè)前綴 prefix 和實(shí)際的結(jié)果 result。

清單 9. 帶前綴的處理結(jié)果

public class PrefixedResult {
 private final String prefix;
 private final String result;
 public PrefixedResult(String prefix, String result) {
 this.prefix = prefix;
 this.result = result;
 }
 public String getPrefix() {
 return prefix;
 }
 public String getResult() {
 return result;
 }
}

在代碼清單 10 中,我們?yōu)?PrefixedResult 添加了具體的處理器實(shí)現(xiàn)。該實(shí)現(xiàn)也非常簡(jiǎn)單,只是把結(jié)果按照某個(gè)格式進(jìn)行輸出。

清單 10. PrefixedResult 對(duì)應(yīng)的處理器實(shí)現(xiàn)

@Component
public class PrefixedResultHandler implements ResultHandler<PrefixedResult> {
 
 @Override
 public void handleResult(PrefixedResult result) {
 System.out.printf("%s --> %s%n", result.getPrefix(), result.getResult());
 }
}

在代碼清單 11 中,命令方法 resultHandler 返回的是一個(gè) PrefixedResult 對(duì)象,因此會(huì)被代碼清單 10 中的處理器來進(jìn)行處理,輸出相應(yīng)的結(jié)果。

清單 11. 使用 PrefixedResult 的命令

@ShellComponent
public class CustomResultHandlerApp {
 @ShellMethod("Result handler")
 public PrefixedResult resultHandler() {
 return new PrefixedResult("PRE", "Hello!");
 }
}

代碼清單 12 給出了具體的命令運(yùn)行結(jié)果。

清單 12. 命令的處理結(jié)果

myshell=>result-handler
PRE --> Hello!

自定義提示符

在啟動(dòng)命令行應(yīng)用時(shí),會(huì)發(fā)現(xiàn)該應(yīng)用使用的是默認(rèn)提示符"shell:>"。該提示符是可以定制的,只需要提供接口 org.springframework.shell.jline.PromptProvider 的實(shí)現(xiàn)即可。接口 PromptProvider 中只有一個(gè)方法,用來返回類型為 org.jline.utils.AttributedString 的提示符。在代碼清單 13 中,我們定義了一個(gè) PromptProvider 接口的實(shí)現(xiàn)類,并使用"myshell=>"作為提示符,而且顏色為藍(lán)色。

清單 13. 自定義提示符

@Bean
public PromptProvider promptProvider() {
 return () -> new AttributedString("myshell=>",
  AttributedStyle.DEFAULT.foreground(AttributedStyle.BLUE));
}

動(dòng)態(tài)命令可用性

前面所創(chuàng)建的命令都是一直可用的。只要應(yīng)用啟動(dòng)起來,就可以使用這些命令。不過有些命令的可用性可能取決于應(yīng)用的內(nèi)部狀態(tài),只有內(nèi)部狀態(tài)滿足時(shí),才可以使用這些命令。對(duì)于這些命令,Spring Shell 提供了類 org.springframework.shell.Availability 來表示命令的可用性。通過類 Availability 的靜態(tài)方法 available()和 unavailable()來分別創(chuàng)建表示命令可用和不可用的 Availability 對(duì)象。

在代碼清單 14 中,我們創(chuàng)建了兩個(gè)命令方法 runOnce()和 runAgain()。變量 run 作為內(nèi)部狀態(tài)。在運(yùn)行 runOnce()之后,變量 run 的值變?yōu)?true。命令 runAgain 的可用性由方法 runAgainAvailability()來確定。該方法根據(jù)變量 run 的值來決定 runAgain 是否可用。按照命名慣例,檢查命令可用性的方法的名稱是在命令方法名稱之后加上 Availability 后綴。如果需要使用不同的方法名稱,或是由一個(gè)檢查方法控制多個(gè)方法,可以在檢查方法上添加注解@ShellMethodAvailability 來聲明其控制的方法名稱。

清單 14. 動(dòng)態(tài)命令可用性

@ShellComponent
public class RunTwiceToEnableApp {
 private boolean run = false;
 @ShellMethod("Run once")
 public void runOnce() {
 this.run = true;
 }
 @ShellMethod("Run again")
 public void runAgain() {
 System.out.println("Run!");
 }
 public Availability runAgainAvailability() {
 return run
  ? Availability.available()
  : Availability.unavailable("You should run runOnce first!");
 }
}

輸入?yún)?shù)轉(zhuǎn)換

之前的@ShellMethod 標(biāo)注的方法使用的都是簡(jiǎn)單類型的參數(shù)。Spring Shell 通過 Spring 框架的類型轉(zhuǎn)換系統(tǒng)來進(jìn)行參數(shù)類型的轉(zhuǎn)換。Spring 框架已經(jīng)內(nèi)置提供了對(duì)常用類型的轉(zhuǎn)換邏輯,包括原始類型、String 類型、數(shù)組類型、集合類型、Java 8 的 Optional 類型、以及日期和時(shí)間類型等。我們可以通過 Spring 框架提供的擴(kuò)展機(jī)制來添加自定義的轉(zhuǎn)換實(shí)現(xiàn)。

代碼清單 15 中的 User 類是作為示例的一個(gè)領(lǐng)域?qū)ο螅?id 和 name 兩個(gè)屬性。

清單 15. User

public class User {
 private final String id;
 private final String name;
 public User(String id, String name) {
 this.id = id;
 this.name = name;
 }
 public String getName() {
 return name;
 }
}

代碼清單 16 中的 UserService 用來根據(jù) id 來查找對(duì)應(yīng)的 User 對(duì)象。作為示例,UserService 只是簡(jiǎn)單使用一個(gè) HashMap 來保存作為測(cè)試的 User 對(duì)象。

清單 16. UserService

public class UserService {
 private final Map<String, User> users = new HashMap<>();
 public UserService() {
 users.put("alex", new User("alex", "Alex"));
 users.put("bob", new User("bob", "Bob"));
 }
 public User findUser(String id) {
 return users.get(id);
 }
}

在代碼清單 17 中,UserConverter 實(shí)現(xiàn)了 Spring 中的 Converter 接口并添加了從 String 到 User 對(duì)象的轉(zhuǎn)換邏輯,即通過 UserService 來進(jìn)行查找。

清單 17. 使用類型轉(zhuǎn)換

@Component
public class UserConverter implements Converter<String, User> {
 private final UserService userService = new UserService();
 @Override
 public User convert(String source) {
 return userService.findUser(source);
 }
}

在代碼清單 18 中,命令方法 user 的參數(shù)是 User 類型。當(dāng)運(yùn)行命令"user alex"時(shí),輸入?yún)?shù) alex 會(huì)通過代碼清單 17 中的類型轉(zhuǎn)換服務(wù)轉(zhuǎn)換成對(duì)應(yīng)的 User 對(duì)象,然后輸出 User 對(duì)象的屬性值 name。如果找不到與輸入?yún)?shù)值對(duì)應(yīng)的 User 對(duì)象,則輸出"User not found"。

清單 18. 使用類型轉(zhuǎn)換的命令

@ShellComponent
public class UserCommandApp {
 @ShellMethod("User")
 public void user(final User user) {
 if (user != null) {
  System.out.println(user.getName());
 } else {
  System.out.println("User not found");
 }
 }
}

命令組織方式

當(dāng)創(chuàng)建很多個(gè)命令時(shí),需要有一種把這些命令組織起來。Spring Shell 提供了不同的方式來對(duì)命令進(jìn)行分組。處于同一分組的命令會(huì)在 help 命令輸出的幫助中出現(xiàn)在一起。默認(rèn)情況下,同一個(gè)類中的命令會(huì)被添加到同一分組中。默認(rèn)的分組名稱根據(jù)對(duì)應(yīng)的 Java 類名來自動(dòng)生成。除了默認(rèn)分組之外,還可以顯式的設(shè)置分組。可以使用@ShellMethod 注解的屬性 group 來指定分組名稱;還可以為包含命令的類添加注解@ShellCommandGroup,則該類中的所有命令都在由@ShellCommandGroup 指定的分組中;還可以把@ShellCommandGroup 注解添加到包聲明中,則該包中的所有命令都在由@ShellCommandGroup 指定的分組中。

在代碼清單 19 中,通過@ShellCommandGroup 為命令所在類添加了自定義的分組名稱 Special。其中的方法 command2 則通過@ShellMethod 的 group 屬性指定了不同的分組名稱"Basic Group"。

清單 19. 組織命令

@ShellComponent
@ShellCommandGroup("Special")
public class CommandsGroupApp {
 @ShellMethod("Command1")
 public void command1() {}
 @ShellMethod(value = "Command2", group = "Basic Group")
 public void command2() {}
}

圖 1 顯示了示例應(yīng)用的 help 命令的輸出結(jié)果,從中可以看到命令的分組情況。

圖 1. 所有的命令列表

如何通過Spring Shell開發(fā)Java命令行應(yīng)用

commands.png

內(nèi)置命令

Spring Shell 提供了很多內(nèi)置的命令,如下所示。

運(yùn)行 help 命令可以列出來應(yīng)用中的所有命令和對(duì)應(yīng)的描述信息。
運(yùn)行 clear 命令可以進(jìn)行清屏操作。
運(yùn)行 exit 命令可以退出命令行應(yīng)用。
運(yùn)行 script 命令可以執(zhí)行一個(gè)文件中包含的所有命令。
如果不需要某個(gè)內(nèi)置命令,可以通過把上下文環(huán)境中的屬性 spring.shell.command.<command>.enabled 的值設(shè)為 false 來禁用。如果希望禁用全部的內(nèi)置命令,可以把 spring-shell-standard-commands 從 Maven 依賴中排除,如代碼清單 20 所示。

清單 20. 排除內(nèi)置命令對(duì)應(yīng)的 Maven 依賴

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

以上是“如何通過Spring Shell開發(fā)Java命令行應(yīng)用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI