Spring Shell應用程式開發流程解析

buduoduoorg發表於2020-10-28

標記介面

  建立命令的第一步是實現標記介面CommandMarker,並使用Spring的@Component註解對類進行註解(注意一個JIRA問題:提供@CliCommand元註解避免使用標記介面)。使用helloworld示例應用程式中的程式碼為例,HelloWorldCommands類的程式碼如下所示:

1
2
3
4
5
6
@Component
public class HelloWorldCommands implements CommandMarker {
 // use any Spring annotations for Dependency Injection or other Spring interfaces
 // as required.
 // methods with @Cli annotations go here
}
日誌

  目前,日誌記錄是使用JDK日誌記錄的。由於控制檯、JLine和Ansi處理的複雜性,一般都建議將訊息作為返回值的方式顯示在shell視窗。但是,當需要進行日誌記錄時,典型的JDK logger宣告就足夠了。

1
2
3
4
5
6
7
8
@Component
public class HelloWorldCommands implements CommandMarker {
 
 protected final Logger LOG = Logger.getLogger(getClass().getName());
 
 // methods with @Cli annotations go here
 
}
  注意:一般開發人員的職責是為第三方庫處理日誌,應當要減少日誌級別,這樣控制檯或者shell視窗就不會受到日誌訊息的影響。

 CLI註解

  在方法和方法引數上使用了三個註釋,這些註釋定義了與shell互動的主要契約,分別是:

CliAvailabilityIndicator - 放在一個方法前返回一個布林值,表示在shell中是否可以執行一個特定的命令。這個決定通常基於之前執行的命令的歷史。它可以防止在滿足某些先決條件時出現外部命令,例如執行'configuration'命令。
CliCommand - 放置在向shell提供命令的方法上。它的值提供了一個或多個字串,這些字串作為特定命令名的開始。在整個應用程式中,包括所有的外掛中這些必須是唯一的。
CliOption - 放置在命令方法的引數中,允許它預設值宣告引數值為必填的或可選的。
  下面是在命令類中使用這些註解的簡單用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class HelloWorldCommands implements CommandMarker {
 
 @CliAvailabilityIndicator({"hw simple"})
 public boolean isCommandAvailable() {
  return true;
 }
 
 @CliCommand(value = "hw simple", help = "Print a simple hello world message")
 public String simple(
  @CliOption(key = { "message" }, mandatory = true, help = "The hello world message")
  final String message,
 
  @CliOption(key = { "location" }, mandatory = false,
        help = "Where you are saying hello", specifiedDefaultValue="At work")
  final String location) {
 
  return "Message = [" + message + "] Location = [" + location + "]";
 
 }
}
註解@CliAvailabilityIndicator方法返回true,這是這個類中暴露給shell呼叫的唯一的命令。如果類中有更多的命令,則將它們作為逗號分隔值列出。
  @CliCommand註解是建立shell命令'hw simple'。幫助訊息是如果您使用幫助命令'help'將會列印什麼內容。這裡定義方法名是“simple”,但它可以是任何自定義的名稱。
  @CliOption註解在每個命令的引數。您需要決定哪些引數是必需的,哪些是可選的,如果它們是可選的,則有一個預設值。在本例中,該命令有兩個引數:訊息'message'和位置'location'。需要使用訊息選項,並提供一個幫助訊息,以便在為該命令完成任務時為使用者提供指導。
  “simple”方法的實現很簡單,只是一個日誌語句,但這是通常呼叫的其他物件,這些物件是通過Spring注入到類中的,然後可以實現複雜的功能。
  本例中的方法引數型別是String,它不會出現型別轉換的任何問題。您可以指定任何的物件型別以及基本資料型別,如int, float等。對所有型別以外由預設shell(基本資料型別, Date, File)需要在您的外掛中與容器的轉換器介面org.springframework.shell.core.Converter 註冊它的轉換。
  注意,方法返回引數可以是非void,在我們的示例中,它是我們想要顯示的實際訊息。返回非void型別時,shell將顯示為它的toString()字元。

測試shell命令

  執行測試的shell命令,您可以例項化shell在一個測試用例中執行命令,然後在返回值CommandResult執行斷言。一個簡單的基類設定如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class AbstractShellIntegrationTest {
 
 private static JLineShellComponent shell;
 
 @BeforeClass
 public static void startUp() throws InterruptedException {
 Bootstrap bootstrap = new Bootstrap();
 shell = bootstrap.getJLineShellComponent();
 }
 
 @AfterClass
 public static void shutdown() {
 shell.stop();
 }
 
 public static JLineShellComponent getShell() {
 return shell;
 }
 
}
  這裡有一個測試日期命令的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BuiltInCommandTests extends AbstractShellIntegrationTest {
 
 @Test
 public void dateTest() throws ParseException {
 
 //Execute command
 CommandResult cr = getShell().executeCommand("date");
 
 //Get result
 DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL,Locale.US);
 Date result = df.parse(cr.getResult().toString());
 
 //Make assertions - DateMaters is an external dependency not shown here.
 Date now = new Date();
 MatcherAssert.assertThat(now, DateMatchers.within(5, TimeUnit.SECONDS, result));
 }
}
  CommandResult的getResult方法返回的 java.lang.Class將匹配與@CliCommand註解方法的返回值。您應該向適當的型別轉換,以幫助執行您的斷言。

構建和執行shell

  在我們看來,構建和執行shell最簡單的方法是剪下和貼上指令碼。這將使來自於分級的應用程式外掛建立一個bin目錄,該目錄帶有用於windows和Unix的啟動指令碼,並將所有依賴jar放在lib目錄中。Maven有一個類似的外掛——AppAssembler外掛。
  shell的主類是org.springframework.shell.Bootstrap的。只要您在類路徑上放置其他的外掛,或者是獨立開發的,引導類就會將它們合併到shell中。

相關文章