4. 命令

在本节中,我们将介绍实际的命令注册并保留命令选项 和执行以供稍后在文档中执行。您可以在 命令注册 中找到更多详细信息。spring-doc.cn

4.1. 注册

有两种不同的方法可以定义命令:通过注释模型和 通过编程模型。在 Comments 模型中,定义方法 ,并使用特定注释对类和方法进行注释。 在编程模型中,使用一种更底层的方法,定义命令 注册(作为 Bean 或通过动态注册到命令目录)。spring-doc.cn

4.1.1. 标注模型

当您使用标准 API 时,bean 上的方法将转换为可执行命令,前提是:spring-doc.cn

  • Bean 类带有 Comments。(这用于限制 bean 的集合 被考虑的。@ShellComponentspring-doc.cn

  • 该方法带有 Comments。@ShellMethodspring-doc.cn

这是一个构造型注释,它本身是用 .因此, 除了过滤机制之外,您还可以使用它来声明 bean(例如,通过使用)。@ShellComponent@Component@ComponentScanspring-doc.cn

您可以使用 Comments 的属性来定制创建的 Bean 的名称。valuespring-doc.cn

@ShellComponent
static class MyCommands {

	@ShellMethod
	public void mycommand() {
	}
}

注解唯一需要的属性是它的属性,它应该有 一个简短的一句话描述,说明命令的作用。这样,您的用户就可以 无需离开 shell 即可获得有关命令的一致帮助(请参阅帮助)。@ShellMethodvaluespring-doc.cn

命令的描述应该简短 — 不超过一两句话。为了更好 consistency,它应该以大写字母开头,以句点结尾。

默认情况下,您无需指定命令的键(即应使用的单词) 在 shell 中调用它)。方法的名称用作 command 键,将 camelCase 名称转换为 虚线、GNU 样式的名称(例如,变为 )。sayHello()say-hellospring-doc.cn

但是,您可以使用 annotation 的属性显式设置 command 键:keyspring-doc.cn

@ShellMethod(value = "Add numbers.", key = "sum")
public int add(int a, int b) {
	return a + b;
}
该属性接受多个值。 如果为单个方法设置多个键,则命令将使用这些不同的别名注册。key
Command 键几乎可以包含任何字符,包括空格。不过,在想出名字时, 请记住,一致性通常受到用户的赞赏。也就是说,您应该避免将虚线名称与 带空格的名称和其他不一致。

4.1.2. 编程模型

在编程模型中, 定义为 ,并且它会自动注册:CommandRegistration@Beanspring-doc.cn

@Bean
CommandRegistration commandRegistration() {
	return CommandRegistration.builder()
		.command("mycommand")
		.build();
}

4.2. 组织命令

当你的 shell 开始提供大量功能时,你最终可能会遇到 使用大量命令,这可能会让您的用户感到困惑。通过键入 , 他们会看到一个令人生畏的命令列表,按字母顺序排列, 这可能并不总是显示可用命令的最佳方式。helpspring-doc.cn

为了减少这种可能的混淆, Spring Shell 提供了将命令分组在一起的能力。 具有合理的默认值。然后,相关命令将位于同一组中(例如,) 并一起显示在帮助屏幕和其他地方。User Management Commandsspring-doc.cn

默认情况下,命令根据它们在其中实现的类进行分组。 将 camelCase 类名转换为单独的单词(so 变为 )。 这是一个明智的默认值,因为相关命令通常已经在类中了。 ,因为他们需要使用相同的协作对象。URLRelatedCommandsURL Related Commandsspring-doc.cn

但是,如果此行为不适合您,则可以将组替换为 命令,按优先级顺序排列:spring-doc.cn

  1. 在注释中指定 a。group()@ShellMethodspring-doc.cn

  2. 将 a 放在定义命令的类上。这适用 该类中定义的所有命令的组(除非被覆盖,如前所述)。@ShellCommandGroupspring-doc.cn

  3. 在包上放置 (通过 ) 其中定义了命令。这适用于 package 中(除非在方法或类级别被覆盖,如前所述)。@ShellCommandGrouppackage-info.javaspring-doc.cn

下面的清单显示了一个示例:spring-doc.cn

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. 动态命令可用性

由于应用程序的内部状态,注册的命令并不总是有意义。 例如,可能有一个命令,但只有在用户在远程 服务器。现在,如果用户尝试使用该命令,shell 应该解释 命令存在,但当时不可用。 Spring Shell 允许你这样做,甚至允许你提供 命令不可用。downloadconnectdownloadspring-doc.cn

命令有三种可能的方式来指示可用性。 它们都使用返回 . 请考虑以下示例:Availabilityspring-doc.cn

@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");
    }
}

该方法用于连接到服务器(省略详细信息),从而更改状态 完成后通过布尔值执行命令。 由于存在,该命令在用户连接之前标记为不可用 的方法名称与名称中带有后缀的命令方法完全相同。 该方法返回 的实例 , 使用两个工厂方法之一构造。 如果该命令不可用,则必须提供解释。 现在,如果用户尝试在未连接的情况下调用命令,则情况如下:connectconnecteddownloaddownloadAvailabilityAvailabilityspring-doc.cn

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.

集成帮助中还使用了有关当前不可用命令的信息。请参阅帮助spring-doc.cn

如果将命令不可用时提供的原因附加到 “Because” 之后,则应该读起来很不错。spring-doc.cn

您不应以大写字母开头或添加最后一个句点spring-doc.cn

如果在命令方法名称后命名 availability 方法不适合您,您可以 可以使用注释提供显式名称:@ShellMethodAvailabilityspring-doc.cn

    @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 名称必须匹配

最后,通常情况下,同一类中的多个命令共享相同的内部状态,因此, 应全部作为一个组可用或不可用。Spring Shell 允许你翻转事情,将 Comments 放在 availability 方法上,指定它控制的命令的名称,而不必坚持所有命令方法:@ShellMethodAvailability@ShellMethodAvailabiltyspring-doc.cn

    @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");
    }

该属性的默认值为 .这个特别的 通配符匹配所有命令名称。这样就可以轻松打开或关闭单个类的所有命令 使用单一可用性方法:@ShellMethodAvailability.value()*spring-doc.cn

@ShellComponent
public 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 对如何编写命令和如何组织类没有施加很多约束。 但是,将相关命令放在同一个类中通常是一种很好的做法,并且可用性指示符 可以从中受益。

4.4. 退出代码

许多命令行应用程序(如果适用)会返回运行环境的退出代码 可用于区分命令是否已成功执行。在 this 中,这主要与命令在非交互模式下运行时有关,这意味着一个命令 始终使用 .spring-shellspring-shellspring-doc.cn

退出代码的默认行为如下:spring-doc.cn

每个 Exception 都可以定义自己的 Exception退出代码之间的映射。 本质上,我们受制于有关退出代码的功能,并且简单地 融入其中。CommandRegistrationSpring Bootspring-doc.cn

假设下面有一个异常 show ,它将从命令中抛出:spring-doc.cn

static class MyException extends RuntimeException {

	private final int code;

	MyException(String msg, int code) {
		super(msg);
		this.code = code;
	}

	public int getCode() {
		return code;
	}
}

可以在 和 退出代码之间定义一个映射函数。您还可以 只需配置一个退出代码,这只是配置中的语法糖。Throwablespring-doc.cn

CommandRegistration.builder()
	.withExitCode()
		.map(MyException.class, 3)
		.map(t -> {
			if (t instanceof MyException) {
				return ((MyException) t).getCode();
			}
			return 0;
		})
		.and()
	.build();
无法使用基于注释的配置自定义退出代码