命令可用性

注册的命令并非总是有效,这取决于应用程序的内部状态。例如,可能有一个download命令,但它只有在用户已通过connect连接到远程服务器后才可用。现在,如果用户尝试使用download命令,Shell 应该解释该命令存在,但当前不可用。Spring Shell 允许你做到这一点,甚至可以提供命令不可用的简短解释。

编程式

通过编程式注册,你可以使用 availability 方法,该方法接受一个 Supplier<Availability>

private boolean connected;

@Bean
public CommandRegistration connect(
		CommandRegistration.BuilderSupplier builder) {
	return builder.get()
		.command("connect")
		.withOption()
			.longNames("connected")
			.required()
			.type(boolean.class)
			.and()
		.withTarget()
			.consumer(ctx -> {
				boolean connected = ctx.getOptionValue("connected");
				this.connected = connected;
			})
			.and()
		.build();
}

@Bean
public CommandRegistration download(
		CommandRegistration.BuilderSupplier builder) {
	return builder.get()
		.command("download")
		.availability(() -> {
			return connected
				? Availability.available()
				: Availability.unavailable("you are not connected");
		})
		.withTarget()
			.consumer(ctx -> {
				// do something
			})
			.and()
		.build();
}

注解式

对于基于注解的命令,你可以结合使用 @CommandAvailabilityAvailabilityProvider

@Command
class MyCommands {

	private boolean connected;

	@Command(command = "connect")
	public void connect(String user, String password) {
		connected = true;
	}


	@Command(command = "download")
	@CommandAvailability(provider = "downloadAvailability")
	public void download(
	) {
		// do something
	}

	@Bean
	public AvailabilityProvider downloadAvailability() {
		return () -> connected
			? Availability.available()
			: Availability.unavailable("you are not connected");
	}
}

遗留注解式

命令有三种可能的方式来指示可用性。它们都使用一个无参方法,该方法返回一个 Availability 实例。请看下面的例子

@ShellComponent
public class MyCommands {

	private boolean connected;

	@ShellMethod("Connect to the server.")
	public void connect(String user, String password) {
		// do something
		connected = true;
	}

	@ShellMethod("Download the nuclear codes.")
	public void download() {
		// do something
	}

	public Availability downloadAvailability() {
		return connected
			? Availability.available()
			: Availability.unavailable("you are not connected");
	}
}

connect 方法用于连接到服务器(省略细节),连接完成后通过 connected 布尔值改变命令状态。通过存在一个与 download 命令方法同名且带有 Availability 后缀的方法,download 命令被标记为不可用,直到用户连接成功。该方法返回一个 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”(因为)后面,应该读起来顺畅。

你不应该以大写字母开头或添加句号

如果将可用性方法命名为与命令方法同名不适合你,可以通过使用 @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");
}

@ShellMethodAvailability.value() 属性的默认值为 *。这个特殊的通配符匹配所有命令名称。这使得使用单个可用性方法控制单个类的所有命令变得容易。

@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 对如何编写命令和组织类没有施加太多限制。然而,通常将相关命令放在同一个类中是一个很好的实践,可用性指示器也能从中受益。