4. 命令
在本节中,我们将介绍实际的命令注册并保留命令选项 和执行以供稍后在文档中执行。您可以在 命令注册 中找到更多详细信息。
4.1. 注册
有两种不同的方法可以定义命令:通过注释模型和 通过编程模型。在 Comments 模型中,定义方法 ,并使用特定注释对类和方法进行注释。 在编程模型中,使用一种更底层的方法,定义命令 注册(作为 Bean 或通过动态注册到命令目录)。
4.1.1. 标注模型
当您使用标准 API 时,bean 上的方法将转换为可执行命令,前提是:
-
Bean 类带有
@ShellComponent
注解。(这用于限制 bean 的集合 被考虑的。 -
该方法带有
@ShellMethod
注解。
这 您可以使用 |
@ShellComponent
static class MyCommands {
@ShellMethod
public void mycommand() {
}
}
的唯一 required 属性@ShellMethod
annotation 是其value
属性,该属性应具有
一个简短的一句话描述,说明命令的作用。这样,您的用户就可以
无需离开 shell 即可获得有关命令的一致帮助(请参阅帮助)。
命令的描述应该简短 — 不超过一两句话。为了更好 consistency,它应该以大写字母开头,以句点结尾。 |
默认情况下,您无需指定命令的键(即应使用的单词)
在 shell 中调用它)。方法的名称用作 command 键,将 camelCase 名称转换为
虚线、GNU 样式、名称(例如sayHello()
成为say-hello
).
但是,您可以使用key
注解的属性:
@ShellMethod(value = "Add numbers.", key = "sum")
public int add(int a, int b) {
return a + b;
}
这key 属性接受多个值。
如果为单个方法设置多个键,则命令将使用这些不同的别名注册。 |
Command 键几乎可以包含任何字符,包括空格。不过,在想出名字时, 请记住,一致性通常受到用户的赞赏。也就是说,您应该避免将虚线名称与 带空格的名称和其他不一致。 |
4.1.2. 编程模型
在程序化模型中,CommandRegistration
可以定义为@Bean
它将自动注册。
@Bean
CommandRegistration commandRegistration() {
return CommandRegistration.builder()
.command("mycommand")
.build();
}
如果所有命令都有共同点,则 创建一个 CommandRegistration.BuilderSupplier,它可以 被自动装配。此供应商的默认实现返回 一个新的构建器,因此您无需担心其内部状态。
自动以编程方式注册的命令 添加 Help Options 中提到的 Help 选项。 |
如果定义了此供应商类型的 bean,则 auto-configuration 将退后,为您提供重新定义默认功能的选项。
@Bean
CommandRegistration commandRegistration(CommandRegistration.BuilderSupplier builder) {
return builder.get()
.command("mycommand")
.build();
}
CommandRegistrationCustomizer
如果你想集中
修改上述供应商提供的 Builder 实例。
@Bean
CommandRegistrationCustomizer commandRegistrationCustomizerExample() {
return builder -> {
// customize instance of CommandRegistration.Builder
};
}
4.2. 组织命令
当你的 shell 开始提供大量功能时,你最终可能会遇到
使用大量命令,这可能会让您的用户感到困惑。通过键入help
,
他们会看到一个令人生畏的命令列表,按字母顺序排列,
这可能并不总是显示可用命令的最佳方式。
为了减少这种可能的混淆, Spring Shell 提供了将命令分组在一起的能力。
具有合理的默认值。然后,相关命令将位于同一组中(例如User Management Commands
)
并一起显示在帮助屏幕和其他地方。
默认情况下,命令根据它们在其中实现的类进行分组。
将 camelCase 类名转换为单独的单词(因此URLRelatedCommands
成为URL Related Commands
).
这是一个明智的默认值,因为相关命令通常已经在类中了。
,因为他们需要使用相同的协作对象。
但是,如果此行为不适合您,则可以将组替换为 命令,按优先级顺序排列:
-
指定
group()
在@ShellMethod
注解。 -
放置
@ShellCommandGroup
在定义命令的类上。这适用 该类中定义的所有命令的组(除非被覆盖,如前所述)。 -
放置
@ShellCommandGroup
在包上(通过package-info.java
) 其中定义了命令。这适用于 package 中(除非在方法或类级别被覆盖,如前所述)。
下面的清单显示了一个示例:
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() {}
}
4.3. 动态命令可用性
由于应用程序的内部状态,注册的命令并不总是有意义。
例如,可能有一个download
命令,但只有在用户使用了connect
在远程
服务器。现在,如果用户尝试使用download
命令,shell 应该解释
命令存在,但当时不可用。
Spring Shell 允许你这样做,甚至允许你提供
命令不可用。
命令有三种可能的方式来指示可用性。
它们都使用一个 no-arg 方法,该方法返回一个Availability
.
请考虑以下示例:
@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");
}
}
这connect
method 连接到服务器(省略细节),从而更改状态
的命令中connected
boolean 值。
这download
命令标记为不可用,直到用户已连接,这要归功于
与名称完全相同的方法download
command 方法替换为Availability
suffix 在其名称中。
该方法返回Availability
,使用两种工厂方法之一构造。
如果该命令不可用,则必须提供解释。
现在,如果用户尝试在未连接的情况下调用命令,则会出现以下情况:
shell:>download
Command '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.
集成帮助中还使用了有关当前不可用命令的信息。请参阅帮助。
如果将命令不可用时提供的原因附加到 “Because” 之后,则应该读起来很不错。 您不应以大写字母开头或添加最后一个句点 |
如果在命令方法名称后命名 availability 方法不适合您,您可以
可以使用@ShellMethodAvailability
注解:
@ShellMethod("Download the nuclear codes.")
@ShellMethodAvailability("availabilityCheck") (1)
public void download() {
[...]
}
public Availability availabilityCheck() { (1)
return connected
? Availability.available()
: Availability.unavailable("you are not connected");
}
1 | 名称必须匹配 |
最后,通常情况下,同一类中的多个命令共享相同的内部状态,因此,
应全部作为一个组可用或不可用。不必粘贴@ShellMethodAvailability
在所有命令方法上,Spring Shell 都允许您翻转事物并将@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");
}
的
|
Spring Shell 对如何编写命令和如何组织类没有施加很多约束。 但是,将相关命令放在同一个类中通常是一种很好的做法,并且可用性指示符 可以从中受益。 |
4.4. 异常处理
异常发生在用户代码中,无论它是有意还是无意。本节介绍
如何spring-shell
处理异常并提供说明和最佳实践
与它合作。
许多命令行应用程序(如果适用)会返回运行环境的退出代码
可用于区分命令是否已成功执行。在spring-shell
这主要与命令在非交互模式下运行时有关,这意味着一个命令
总是使用spring-shell
.请注意,退出代码始终与非交互式 shell 相关。
4.4.1. 异常解决
未处理的异常将冒泡到 shell 的ResultHandlerService
然后最终
由ResultHandler
.链ExceptionResolver
实现
可用于解决异常,并为您提供返回消息的灵活性
与 Exit 代码一起放入 console 中,退出代码包含在CommandHandlingResult
.CommandHandlingResult
可能包含消息和/或退出代码。
static class CustomExceptionResolver implements CommandExceptionResolver {
@Override
public CommandHandlingResult resolve(Exception e) {
if (e instanceof CustomException) {
return CommandHandlingResult.of("Hi, handled exception\n", 42);
}
return null;
}
}
CommandExceptionResolver
实现可以全局定义为 bean。
@Bean
CustomExceptionResolver customExceptionResolver() {
return new CustomExceptionResolver();
}
或定义CommandRegistration
如果它仅适用于特定命令本身。
CommandRegistration.builder()
.withErrorHandling()
.resolver(new CustomExceptionResolver())
.and()
.build();
使用命令定义的解析程序在全局解析程序之前处理。 |
使用你自己的异常类型,它也可以是 boot 的ExitCodeGenerator
如果
您希望在此处定义退出代码。
static class CustomException extends RuntimeException implements ExitCodeGenerator {
@Override
public int getExitCode() {
return 0;
}
}
一些内置CommandExceptionResolver
bean 注册为处理常见的
命令解析引发的异常。这些 Cookie 使用 中定义的 Order presedence 进行注册CommandExceptionResolver.DEFAULT_PRECEDENCE
.
由于这些 bean 是按给定的顺序使用的,因此@Order
annotation 或Ordered
Interface from 可以像在任何其他 Spring 应用程序中一样使用。这
如果您需要控制自己的 bean 才能使用
在 defaults 之前或之后。
4.4.2. 退出代码映射
退出代码的默认行为如下:
-
命令选项解析中的错误将导致代码为
2
-
任何一般错误都会导致结果代码为
1
-
显然,在任何其他情况下,结果代码都是
0
每CommandRegistration
可以在 Exception 和退出代码之间定义自己的映射。
本质上,我们被Spring Boot
关于退出代码,简单地
融入其中。
假设下面有一个异常 show ,它将从命令中抛出:
static class MyException extends RuntimeException {
private final int code;
MyException(String msg, int code) {
super(msg);
this.code = code;
}
public int getCode() {
return code;
}
}
可以在Throwable
和退出代码。您还可以
只需配置一个类以退出代码,这只是配置中的语法糖。
CommandRegistration.builder()
.withExitCode()
.map(MyException.class, 3)
.map(t -> {
if (t instanceof MyException) {
return ((MyException) t).getCode();
}
return 0;
})
.and()
.build();
无法使用基于注释的配置自定义退出代码 |
4.4.3. @ExceptionResolver
@ShellComponent
类可以具有@ExceptionResolver
处理 Component 异常的方法
方法。这些用于带 Comments 的方法。
该异常可能与正在传播的顶级异常匹配(例如,直接的 IOException 被抛出)或针对包装器异常中的嵌套原因(例如 IOException 包装 在 IllegalStateException 中)。这可以在任意原因级别匹配。
对于匹配的异常类型,最好将目标异常声明为方法参数,如 前面的示例显示了。当多个异常方法匹配时,根异常匹配为 通常优先于 Cause 异常匹配。更具体地说,ExceptionDepthComparator 用于根据异常从引发的异常类型中对异常的深度对异常进行排序。
或者,注解声明可以缩小要匹配的异常类型,因为 以下示例显示:
@ExceptionResolver({ RuntimeException.class })
CommandHandlingResult errorHandler(Exception e) {
// Exception would be type of RuntimeException,
// optionally do something with it
return CommandHandlingResult.of("Hi, handled exception\n", 42);
}
@ExceptionResolver
CommandHandlingResult errorHandler(RuntimeException e) {
return CommandHandlingResult.of("Hi, handled custom exception\n", 42);
}
@ExceptionResolver
也可以返回String
用作 console 的输出。您可以
用@ExitCode
注解来定义返回代码。
@ExceptionResolver
@ExitCode(code = 5)
String errorHandler(Exception e) {
return "Hi, handled exception";
}
@ExceptionResolver
跟void
return 类型会自动作为 Handled Exception 处理。
然后,您还可以定义@ExitCode
并使用Terminal
如果你需要写点什么
进入控制台。
@ExceptionResolver
@ExitCode(code = 5)
void errorHandler(Exception e, Terminal terminal) {
PrintWriter writer = terminal.writer();
String msg = "Hi, handled exception " + e.toString();
writer.println(msg);
writer.flush();
}
4.5. 隐藏命令
可以隐藏命令,这在尚未准备好的情况下很方便 Prime Time 用于调试目的,或者您有任何其他不想的原因 宣传它的 presense。
如果您知道 Hidden 命令及其选项,则可以执行它。它被有效地删除 从:
-
帮助列表
-
命令返回 “unknown command” 的帮助页面
-
交互模式下的命令完成
-
Bash 完成
以下是如何将 command 定义为 hidden 的示例。它显示了可用的构建器方法 来定义隐藏状态。
CommandRegistration commandRegistration() {
return CommandRegistration.builder()
.command("mycommand")
// define as hidden
.hidden()
// can be defined via a flag (false)
.hidden(false)
// can be defined via a flag (true)
.hidden(true)
.build();
}
基于注释的配置不支持定义隐藏命令 |
4.6. 帮助选项
Spring Shell 有一个内置的help
命令,但并非都赞成获得命令帮助
从它开始,因为您总是需要使用 Arguments for Target Command 调用它。它
在许多 CLI 框架中,每个命令都有选项 --help 和 -h 来打印命令 help。
默认功能是每个命令都会被修改为具有选项 --help 和 -h,如果它们出现在给定的命令中,则会自动
将 short circuit 命令执行到现有的help
命令无关
键入的其他命令行选项。
以下示例显示了其默认设置。
@Bean
CommandRegistration commandRegistration() {
return CommandRegistration.builder()
.command("mycommand")
.withHelpOptions()
.enabled(true)
.longNames("help")
.shortNames('h')
.command("help")
.and()
.build();
}
可以通过配置选项更改默认行为。
spring:
shell:
help:
enabled: true
long-names: help
short-names: h
command: help
以编程方式或通过注释定义的命令将自动添加 help 选项。使用注释模型,您只能全局关闭,以编程方式 model 提供了修改每个命令的设置的选项。 |
4.7. 交互模式
命令注册可以定义InteractionMode
用于隐藏命令
取决于正在执行的模式 shell。更多相关信息,请参见 交互模式.
您可以通过以下方式定义它CommandRegisration
.
CommandRegistration commandRegistration() {
return CommandRegistration.builder()
.command("mycommand")
// can be defined for all modes
.interactionMode(InteractionMode.ALL)
// can be defined only for interactive
.interactionMode(InteractionMode.INTERACTIVE)
// can be defined only for non-interactive
.interactionMode(InteractionMode.NONINTERACTIVE)
.build();
}
或者使用@ShellMethod
.
@ShellMethod(key = "mycommand", interactionMode = InteractionMode.INTERACTIVE)
public void mycommand() {
}
4.8. 内置命令
4.8.1. 帮助
运行 shell 应用程序通常意味着用户处于图形受限的
环境。此外,虽然在手机时代我们几乎总是保持连接,
访问 Web 浏览器或任何其他富 UI 应用程序(如 PDF 查看器)可能并不总是
是可能的。这就是为什么 shell 命令必须正确地自记录很重要的原因,这就是help
命令进来。
打字help
+ ENTER
列出 shell 已知的所有命令(包括不可用的命令)
以及他们工作的简短描述,类似于以下内容:
my-shell:>help
AVAILABLE COMMANDS
Built-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.
打字help <command>
显示有关命令的更多详细信息,包括可用参数、其
类型、是否为必填项以及其他详细信息。
下面的清单显示了help
命令应用于自身:
my-shell:>help help
NAME
help - Display help about available commands
SYNOPSIS
help --command String
OPTIONS
--command or -C String
The command to obtain help for.
[Optional]
帮助是模板化的,如果需要,可以进行自定义。设置位于spring.shell.command.help
您可以使用的位置enabled
要禁用命令,grouping-mode
取group
或flat
如果要通过拼合来隐藏组
a 结构,command-template
要定义命令帮助输出的模板,commands-template
定义
命令列表的输出。
如果spring.shell.command.help.grouping-mode=flat
设置,则 help 将显示:
my-shell:>help help
AVAILABLE 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.
输出help
和help <commmand>
都使用默认实现进行模板化
可以更改。
选择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
作为模型。
钥匙 | 描述 |
---|---|
|
|
|
commands 变量(请参阅 GroupCommandInfoModel 变量)。 |
|
commands 变量(请参阅 CommandInfoModel 变量)。 |
|
|
钥匙 | 描述 |
---|---|
|
组的名称(如果已设置)。否则为空。 |
|
命令 (如果已设置)。否则为空。Type 是多值,请参阅 CommandInfoModel 变量。 |
钥匙 | 描述 |
---|---|
|
命令的名称(如果已设置)。否则为 null。类型为 string 并包含 full 命令。 |
|
命令的名称(如果已设置)。否则为 null。类型本质上是多值 |
|
可能的别名(如果已设置)。类型是带字符串的多值。 |
|
命令的描述(如果已设置)。否则为 null。 |
|
parameters 变量(如果已设置)。否则为空。Type 是多值,请参阅 CommandParameterInfoModel 变量。 |
|
可用性变量(请参阅 CommandAvailabilityInfoModel 变量)。 |
钥匙 | 描述 |
---|---|
|
参数的类型(如果已设置)。否则为 null。 |
|
参数(如果已设置)。否则为 null。类型是带字符串的多值。 |
|
|
|
参数的描述 (如果已设置)。否则为 null。 |
|
参数的默认值 (如果已设置)。否则为 null。 |
|
|
钥匙 | 描述 |
---|---|
|
|
|
如果设置了 not available (如果已设置),则为原因。否则为 null。 |
4.8.3. 退出
这quit
命令(也别名为exit
) 请求 shell 正常退出
关闭 Spring 应用程序上下文。如果未覆盖,则 JLineHistory
Bean 写入所有
命令复制到磁盘,以便它们在下次启动时再次可用。
4.8.4. 堆栈跟踪
当命令代码中发生异常时,shell 会捕获该异常,并显示一条简单的单行消息 以免用户收到太多信息。 但是,在某些情况下,了解到底发生了什么很重要(尤其是在异常具有嵌套原因时)。
为此, Spring Shell 会记住上次发生的异常,用户稍后可以使用stacktrace
命令在控制台上打印所有详细信息。
4.8.5. 脚本
这script
command 接受本地文件作为参数,并重放在那里找到的命令,一次一个。
从文件中读取的行为与在交互式 shell 中完全一样,因此会考虑以
作为注释并被忽略,而以 trigger line continuation 结尾的行。//
\
4.8.6. 历史
这history
command 显示已执行的命令的历史记录。
您可以使用一些配置选项来配置行为
的历史。历史记录保存在日志文件中,该文件默认处于启用状态,并且可以
通过设置spring.shell.history.enabled
.日志文件的名称
从spring.application.name
并默认为spring-shell.log
,
您可以通过设置spring.shell.history.name
.
默认情况下,将生成一个日志文件到当前工作目录,您可以指定该目录
通过设置spring.shell.config.location
.此属性可以包含
占位符 ({userconfig}
),该目录解析为通用共享配置目录。
运行 Spring Shell 应用程序,查看示例应用程序在使用这些选项时的工作原理。 |
4.8.7. 完成
这completion
Command Set 允许您创建可以使用的脚本文件
使用 AM OS shell 实现来提供补全。这在以下情况下非常有用
使用非交互模式。
目前,唯一的实现是 bash,它适用于bash
子命令。
4.8.8. 版本
这version
命令通过集成到
靴子的BuildProperties
和GitProperties
如果 shell 应用程序中存在这些 cookie。
默认情况下,只显示版本信息,您可以通过配置启用其他信息
选项。
相关设置位于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
,您可以定义
您自己的,如下例所示:
<buildVersion>
此设置将输出如下内容:
X.X.X
您可以将以下属性添加到默认模板渲染中:buildVersion
,buildGroup
,buildGroup
,buildName
,buildTime
,gitShortCommitId
,gitCommitId
,gitBranch
和gitCommitTime
.
4.9. 写入
当需要将某些内容写入您的控制台时,您始终可以
使用 JDK 的System.out
然后直接进入 JDK 自己的流中。
其他推荐的方法是使用 JLine 的Terminal
并从那里获取 Writer 实例。
如果使用目标终端节点,即不需要的 consumer
返回任何给定的CommandContext
包含对Terminal
和 Writer 都可以从那里访问。
CommandRegistration.builder()
.command("example")
.withTarget()
.consumer(ctx -> {
ctx.getTerminal().writer().println("hi");
ctx.getTerminal().writer().flush();
})
.and()
.build();
可以自动装配Terminal
以访问其编写器。
@Autowired
Terminal terminal;
@ShellMethod
public void example() {
terminal.writer().println("hi");
terminal.writer().flush();
}