小編給大家分享一下如何通過Spring Shell開發(fā)Java命令行應用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
創(chuàng)新互聯(lián)公司網站建設公司一直秉承“誠信做人,踏實做事”的原則,不欺瞞客戶,是我們最起碼的底線! 以服務為基礎,以質量求生存,以技術求發(fā)展,成交一個客戶多一個朋友!專注中小微企業(yè)官網定制,做網站、成都做網站,塑造企業(yè)網絡形象打造互聯(lián)網企業(yè)效應。Spring Shell 入門
最簡單的創(chuàng)建 Spring Shell 應用的方式是使用 Spring Boot。從 Spring Initializr 網站(http://start.spring.io/)上創(chuàng)建一個新的基于 Apache Maven 的 Spring Boot 應用,然后添加 Spring Shell 相關的依賴即可。本文介紹的是 Spring Shell 2.0.0.M2 版本,目前還只是 Milestone 版本,因此需要在 pom.xml 中添加 Spring 提供的包含 Milestone 版本工件的 Maven 倉庫,如代碼清單 1 所示。否則的話,Maven 會無法找到相應的工件。
清單 1. 添加 Spring Shell 的 Maven 倉庫
spring-milestone Spring Repository https://repo.spring.io/milestone
在添加了 Spring Shell 的 Maven 倉庫之后,可以在 Spring Boot 項目中添加對于spring-shell-starter 的依賴,如代碼清單 2 所示。
清單 2. 添加 Spring Shell 所需 Maven 依賴
org.springframework.shell spring-shell-starter 2.0.0.M2
我們接著可以創(chuàng)建第一個基于 Spring Shell 的命令行應用。該應用根據輸入的參數來輸出相應的問候語,完整的代碼如清單 3 所示。從代碼清單 3 中可以看到,在 Spring Shell 的幫助下,完整的實現代碼非常簡單。代碼的核心是兩個注解:@ShellComponent 聲明類GreetingApp 是一個 Spring Shell 的組件;@ShellMethod 表示方法 sayHi 是可以在命令行運行的命令。該方法的參數 name 是命令行的輸入參數,而其返回值是命令行執(zhí)行的結果。
清單 3. 輸出問候語的命令行應用
org.springframework.shell spring-shell-starter 2.0.0.M2
接下來我們運行該應用。運行起來之后,該應用直接進入命令行提示界面,我們可以輸入 help 來輸出使用幫助。help 是 Spring Shell 提供的眾多內置命令之一,在列出的命令中,可以看到我們創(chuàng)建的 say-hi 命令。我們輸入"say-hi Alex"來運行該命令,可以看到輸出的結果"Hi Alex"。如果我們直接輸入"say-hi",會看到輸出的錯誤信息,告訴我們參數"--name"是必須的。從上面的例子可以看出,在 Spring Shell 的幫助下,創(chuàng)建一個命令行應用是非常簡單的。很多實用功能都已經默認提供了。在使用 Spring Initializr 創(chuàng)建的 Spring Boot 項目中,默認提供了一個單元測試用例。這個默認的單元測試用例與 Spring Shell 在使用時存在沖突。在進行代碼清單 3 中的項目的 Maven 構建時,該測試用例需要被禁用,否則構建過程會卡住。
參數傳遞與校驗
下面我們討論 Spring Shell 中的參數傳遞和校驗。Spring Shell 支持兩種不同類型的參數,分別是命名參數和位置參數。命名參數有名稱,可以通過類似--arg 的方式來指定;位置參數則按照其在方法的參數列表中的出現位置來進行匹配。命名參數和位置參數可以混合起來使用,不過命名參數的優(yōu)先級更高,會首先匹配命名參數。每個參數都有默認的名稱,與方法中的對應的參數名稱一致。
在代碼清單 4 中的方法有 3 個參數 a、b 和 c。在調用該命令時,可以使用"echo1 --a 1 --b 2 --c 3",也可以使用"echo1 --a 1 2 3"或"echo1 1 3 --b 2"。其效果都是分別把 1,2 和 3 賦值給 a、b 和 c。
清單 4. 包含多個參數的命令方法
@ShellMethod("Echo1") public String echo1(int a, int b, int c) { return String.format("a = %d, b = %d, c = %d", a, b, c); }
如果不希望使用方法的參數名稱作為命令對應參數的名稱,可以通過@ShellOption 來標注所要使用的一個或多個參數名稱。我們可以通過指定多個參數名稱來提供不同的別名。在代碼清單 5 中,為參數 b 指定了一個名稱 boy??梢酝ㄟ^"echo2 1 --boy 2 3"來調用。
清單 5. 指定參數名稱
@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); }
對于命名參數,默認使用的是"--"作為前綴,可以通過@ShellMethod 的屬性 prefix 來設置不同的前綴。方法對應的命令的名稱默認是從方法名稱自動得到的,可以通過屬性 key 來設置不同的名稱,屬性 value 表示的是命令的描述信息。如果參數是可選的,可以通過@ShellOption 的屬性 defaultValue 來設置默認值。在代碼清單 6 中,我們?yōu)榉椒?withDefault 指定了一個命令名稱 default,同時為參數 value 指定了默認值"Hello"。如果直接運行命令"default",輸出的結果是"Value: Hello";如果運行命令"default 123",則輸出的結果是"Value: 123"。
清單 6. 指定方法名稱和參數默認值
@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); } }
一個參數可以對應多個值。通過@ShellOption 屬性 arity 可以指定一個參數所對應的值的數量。這些參數會被添加到一個數組中,可以在方法中訪問。在代碼清單 7 中,方法 echo3 的參數 numbers 的 arity 值是 3,因此可以映射 3 個參數。在運行命令"echo3 1 2 3"時,輸出的結果是"a = 1, b =2, c = 3"。
清單 7. 參數對應多個值
@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]); }
如果參數的類型是布爾類型 Boolean,在調用的時候不需要給出對應的值。當參數出現時就表示值為 true。
Spring Shell 支持對參數的值使用 Bean Validation API 進行驗證。比如我們可以用@Size 來限制字符串的長度,用@Min 和@Max 來限制數值的大小,如代碼清單 8 所示。
清單 8. 校驗參數
@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); } }
結果處理
Spring Shell 在運行時,內部有一個處理循環(huán)。在每個循環(huán)的執(zhí)行過程中,首先讀取用戶的輸入,然后進行相應的處理,最后再把處理的結果輸出。這其中的結果處理是由 org.springframework.shell.ResultHandler 接口來實現的。Spring Shell 中內置提供了對于不同類型結果的處理實現。命令執(zhí)行的結果可能有很多種:如果用戶輸入的參數錯誤,輸出的結果應該是相應的提示信息;如果在命令的執(zhí)行過程中出現了錯誤,則需要輸出相應的錯誤信息;用戶也可能直接退出命令行。Spring Shell 默認使用的處理實現是類 org.springframework.shell.result.IterableResultHandler。IterableResultHandler 負責處理 Iterable 類型的結果對象。對于 Iterable 中包含的每個對象,把實際的處理請求代理給另外一個 ResultHandler 來完成。IterableResultHandler 默認的代理實現是類 org.springframework.shell.result.TypeHierarchyResultHandler。TypeHierarchyResultHandler 其實是一個復合的處理器,它會把對于不同類型結果的 ResultHandler 接口的實現進行注冊,然后根據結果的類型來選擇相應的處理器實現。如果找不到類型完全匹配的處理器實現,則會沿著結果類型的層次結構樹往上查找,直到找到對應的處理器實現。Spring Shell 提供了對于 Object 類型結果的處理實現類 org.springframework.shell.result.DefaultResultHandler,因此所有的結果類型都可以得到處理。DefaultResultHandler 所做的處理只是把 Object 類型轉換成 String,然后輸出到控制臺。
了解了 Spring Shell 對于結果的處理方式之后,我們可以添加自己所需要的特定結果類型的處理實現。代碼清單 9 給了一個作為示例的處理結果類 PrefixedResult。PrefixedResult 中包含一個前綴 prefix 和實際的結果 result。
清單 9. 帶前綴的處理結果
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 添加了具體的處理器實現。該實現也非常簡單,只是把結果按照某個格式進行輸出。
清單 10. PrefixedResult 對應的處理器實現
@Component public class PrefixedResultHandler implements ResultHandler{ @Override public void handleResult(PrefixedResult result) { System.out.printf("%s --> %s%n", result.getPrefix(), result.getResult()); } }
在代碼清單 11 中,命令方法 resultHandler 返回的是一個 PrefixedResult 對象,因此會被代碼清單 10 中的處理器來進行處理,輸出相應的結果。
清單 11. 使用 PrefixedResult 的命令
@ShellComponent public class CustomResultHandlerApp { @ShellMethod("Result handler") public PrefixedResult resultHandler() { return new PrefixedResult("PRE", "Hello!"); } }
代碼清單 12 給出了具體的命令運行結果。
清單 12. 命令的處理結果
myshell=>result-handler PRE --> Hello!
自定義提示符
在啟動命令行應用時,會發(fā)現該應用使用的是默認提示符"shell:>"。該提示符是可以定制的,只需要提供接口 org.springframework.shell.jline.PromptProvider 的實現即可。接口 PromptProvider 中只有一個方法,用來返回類型為 org.jline.utils.AttributedString 的提示符。在代碼清單 13 中,我們定義了一個 PromptProvider 接口的實現類,并使用"myshell=>"作為提示符,而且顏色為藍色。
清單 13. 自定義提示符
@Bean public PromptProvider promptProvider() { return () -> new AttributedString("myshell=>", AttributedStyle.DEFAULT.foreground(AttributedStyle.BLUE)); }
動態(tài)命令可用性
前面所創(chuàng)建的命令都是一直可用的。只要應用啟動起來,就可以使用這些命令。不過有些命令的可用性可能取決于應用的內部狀態(tài),只有內部狀態(tài)滿足時,才可以使用這些命令。對于這些命令,Spring Shell 提供了類 org.springframework.shell.Availability 來表示命令的可用性。通過類 Availability 的靜態(tài)方法 available()和 unavailable()來分別創(chuàng)建表示命令可用和不可用的 Availability 對象。
在代碼清單 14 中,我們創(chuàng)建了兩個命令方法 runOnce()和 runAgain()。變量 run 作為內部狀態(tài)。在運行 runOnce()之后,變量 run 的值變?yōu)?true。命令 runAgain 的可用性由方法 runAgainAvailability()來確定。該方法根據變量 run 的值來決定 runAgain 是否可用。按照命名慣例,檢查命令可用性的方法的名稱是在命令方法名稱之后加上 Availability 后綴。如果需要使用不同的方法名稱,或是由一個檢查方法控制多個方法,可以在檢查方法上添加注解@ShellMethodAvailability 來聲明其控制的方法名稱。
清單 14. 動態(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!"); } }
輸入參數轉換
之前的@ShellMethod 標注的方法使用的都是簡單類型的參數。Spring Shell 通過 Spring 框架的類型轉換系統(tǒng)來進行參數類型的轉換。Spring 框架已經內置提供了對常用類型的轉換邏輯,包括原始類型、String 類型、數組類型、集合類型、Java 8 的 Optional 類型、以及日期和時間類型等。我們可以通過 Spring 框架提供的擴展機制來添加自定義的轉換實現。
代碼清單 15 中的 User 類是作為示例的一個領域對象,包含了 id 和 name 兩個屬性。
清單 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 用來根據 id 來查找對應的 User 對象。作為示例,UserService 只是簡單使用一個 HashMap 來保存作為測試的 User 對象。
清單 16. UserService
public class UserService { private final Mapusers = 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 實現了 Spring 中的 Converter 接口并添加了從 String 到 User 對象的轉換邏輯,即通過 UserService 來進行查找。
清單 17. 使用類型轉換
@Component public class UserConverter implements Converter{ private final UserService userService = new UserService(); @Override public User convert(String source) { return userService.findUser(source); } }
在代碼清單 18 中,命令方法 user 的參數是 User 類型。當運行命令"user alex"時,輸入參數 alex 會通過代碼清單 17 中的類型轉換服務轉換成對應的 User 對象,然后輸出 User 對象的屬性值 name。如果找不到與輸入參數值對應的 User 對象,則輸出"User not found"。
清單 18. 使用類型轉換的命令
@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"); } } }
命令組織方式
當創(chuàng)建很多個命令時,需要有一種把這些命令組織起來。Spring Shell 提供了不同的方式來對命令進行分組。處于同一分組的命令會在 help 命令輸出的幫助中出現在一起。默認情況下,同一個類中的命令會被添加到同一分組中。默認的分組名稱根據對應的 Java 類名來自動生成。除了默認分組之外,還可以顯式的設置分組??梢允褂聾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 顯示了示例應用的 help 命令的輸出結果,從中可以看到命令的分組情況。
圖 1. 所有的命令列表
commands.png
內置命令
Spring Shell 提供了很多內置的命令,如下所示。
運行 help 命令可以列出來應用中的所有命令和對應的描述信息。
運行 clear 命令可以進行清屏操作。
運行 exit 命令可以退出命令行應用。
運行 script 命令可以執(zhí)行一個文件中包含的所有命令。
如果不需要某個內置命令,可以通過把上下文環(huán)境中的屬性 spring.shell.command.
清單 20. 排除內置命令對應的 Maven 依賴
org.springframework.shell spring-shell-starter 2.0.0.M2 org.springframework.shell spring-shell-standard-commands
以上是“如何通過Spring Shell開發(fā)Java命令行應用”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道!