SpringBoot Shell基于JLine库实现了REPL(READ EVAL PRINT LOOP) 模式的命令行工具,给我们提供了方便的使用命令行的工具。
通过https://start.spring.io/创建SpringBoot项目,并添加依赖。
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell-starter</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
随后启动系统可以会自动进入到shell模式.
shell:>
如果想在系统启动时,禁用默认的shell模式,那么在配置文件中增加如下配置[2]。
spring.shell.interactive.enabled=false
当false禁用shell模式,为true时启用shell模式。
通过@ShellComponent注解标注类,@ShellMethod标注方法,表示要创建的命令,方法参数为命令参数。
@ShellComponent
public class MyCommands {
@ShellMethod("Add two integers together.")
public int add(int a,int b){
return a + b;
}
}
在控制台输入命令,会产生相应的输出
shell:>add 1 2
3
默认情况下不需要为命令指定key,方法名将被用来作为命令名称。驼峰式命名奖杯转换为横线连接的名称。例如sayHello()转换为say-hello。输入命令时需要使用say-hello。如果需要显示设定命令名可以使用注解的key属性。
@ShellMethod(value = "Add numbers.", key = "sum")
public int add(int a, int b) {
return a + b;
}
shell:>sum 1 2
3
key可以接收多个参数,这个不同的参数将作为命令的别名来使用。
@ShellComponent
public class MyCommands {
@ShellMethod(value = "Add two integers together.",key = {"sum","s"})
public int add(int a,int b){
return a + b;
}
}
shell:>s 1 2
3
echo命令的调用有两种方式
1.使用参数关键字调用,通过 --参数名 值 形式调用
2.不使用参数关键字,而直接穿入参数,并按参数出现的顺序来调用命令。
@ShellMethod("Display stuff.")
public String echo(int a, int b, int c) {
return String.format("You said a=%d, b=%d, c=%d", a, b, c);
}
shell:>echo 1 2 3
You said a=1, b=2, c=3
shell:>echo --a 1 --b 2 --c 3
You said a=1, b=2, c=3
shell:>echo --b 2 --c 3 --a 1
You said a=1, b=2, c=3
shell:>echo --a 1 2 3
You said a=1, b=2, c=3
shell:>echo 1 --c 3 2
You said a=1, b=2, c=3
1.直接写参数顺序来调用,a为1 ,b为2,c为3
2.通过参数关键字来调用,–a指定为1 --b为2,–c为3
3.通过参数关键字来调用,并改变顺序。
4,5通过两种方式混合来调用,顺序可通过输出清楚看明白。
上面的例子,我们通过参数 --参数名 来设置命令对应参数的值。此外也可以通过@ShellOption注解自定义参数名及前缀。而未通过ShellOption定义的参数,将通过注解参数prefix修改变默认前缀。
@ShellMethod(value = "Display stuff.", prefix="-")
public String echo(int a, int b, @ShellOption("--third") int c) {
return String.format("You said a=%d, b=%d, c=%d", a, b, c);
}
shell:>echo -a 1 -b 2 --third 3
You said a=1, b=2, c=3
@ShellOption({"–third","-t"})也可以接收多个参数
shell:>echo -a 1 -b 2 -t 3
You said a=1, b=2, c=3
可以通过@ShellOption的参数defaultValue指定默认值。
@ShellMethod("Say hello.")
public String greet(@ShellOption(defaultValue="World") String who) {
return "Hello " + who;
}
shell:>greet
Hello World
有时一个参数可能会接收多个值,这时就需要用到@ShellOption注解的arity参数。
@ShellMethod("Add numbers.")
public float add(@ShellOption(arity = 3) float[] numbers) {
return numbers[0] + numbers[1] + numbers[2];
}
shell:>add 1 2 3.3
6.3
shell:>add --numbers 1 2 3.3
6.3
@ShellMethod("Terminate the system")
public String shutdown(boolean force) {
return "You said " + force;
}
shell:>shutdown
You said false
shell:>shutdown --force
You said true
boolean参数默认值为false,当指定参数选项 --force时输出为true。
此外可以通过@ShellOption(arity = 1 ,defaultValue = “true”)boolean参数默认值。
@ShellMethod("Terminate the system")
public String shutdown(@ShellOption(arity = 1 ,defaultValue = "true") boolean force) {
return "You said " + force;
}
shell:>shutdown false
You said false
shell:>shutdown
You said true
Spring shell默认通过空格分割命令,如果命令中包含空格,那么需要通过单引号或双引来指定使用的参数。
@ShellMethod("Prints what has been entered")
public String echo(String what) {
return "You said " + what;
}
shell:>echo "hello word"
You said hello word
如果参数中有单引号,那么可以通过双引号包围参数,两者混合使用。
shell:>echo "hello i'm here"
You said hello i'm here
如果只用一种类型引号,且参数中还需要用到这种引号作为参数,那么需要使用转义符号。
shell:>echo 'i\'m here'
You said i'm here
此外还可以对空格转义。
shell:>echo this\ is
You said this is
Spring shell集成了Bean Validation API,支持参数校验。校验将在命令执行前进行。
@ShellMethod("Change password.")
public String changePassword(@Size(min = 8,max = 40)String password) {
return "Password successfully set to " + password;
}
shell:>change-password as
The following constraints were not met:
--password string : 个数必须在8和40之间 (You passed 'as')
某些命令必的执行需要前置命令执行完毕才可以执行。例如 下载文件需要先执行连接(connect),接着再执行(download)。Spring shell提供了3种方式执行可用性验证。利用Availability对象实例来实现检测。
下面的例子中,connect方法被调用后,变量connected被设置为true,download方法才可以正常调用。由于增加了方法downloadAvailability,即为方法名download增加了后缀Availability,在download被调用时会检查downloadAvailability返回的Availability实例。若不可用会返回提示信息。
@ShellComponent
public class MyCommands {
private boolean connected;
@ShellMethod("connection server")
public void connect(String user,String password) {
connected = true;
}
@ShellMethod("download nuclear code")
public void download() {
}
Availability downloadAvailability() {
return connected ? Availability.available() :
Availability.unavailable("You are not connected");
}
}
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.
shell:>connect
Parameter '--user string' should be specified
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.
shell:>connect m n
shell:>download
shell:>
若不便于为方法增加Availability后缀来添加检查方法。还可以使@ShellMethodAvailability注解显式指定检查方法。
@ShellComponent
public class MyCommands {
private boolean connected;
@ShellMethod("connection server")
public void connect(String user,String password) {
connected = true;
}
@ShellMethod("download nuclear code")
@ShellMethodAvailability("vailabilityCheck")
public void download() {
}
Availability vailabilityCheck() {
return connected ? Availability.available() :
Availability.unavailable("You are not connected");
}
}
实际使用中,存在多个后继命令,在前置命令被执行后才可以使用。那么通过@ShellMethodAvailability指定多个命令即可。
@ShellComponent
public class MyCommands {
private boolean connected;
@ShellMethod("connection server")
public void connect(String user,String password) {
connected = true;
}
@ShellMethod("download nuclear code")
public void download() {
}
@ShellMethod("disconnect")
public void disconnected() {
}
@ShellMethodAvailability({"download","disconnected"})
Availability vailabilityCheck() {
return connected ? Availability.available() :
Availability.unavailable("You are not connected");
}
}
当输入help命令时,会看到系统所支持的命令信息。
AVAILABLE COMMANDS
Built-In Commands
clear: Clear the shell screen.
exit, quit: Exit the shell.
help: Display help about available commands.
script: Read and execute commands from a file.
stacktrace: Display the full stacktrace of the last error.
My Commands
add: Add numbers.
change-password: Change password.
connect: connection server
disconnected: disconnect
download: download nuclear code
echo: Prints what has been entered
greet: Say hello.
s, sum: Add two integers together.
shutdown: Terminate the system
shell:>
命令默认情况下是按照所在类的类名来组织的。此外可以按优先级顺序按如下方式下自定义。最后可输入help命令来验证分组结果。
1.在@ShellMethod注解中通过group属性来指定。
2.在class上使用@ShellCommandGroup注解来定义。
3.在package-info.java文件中使用@ShellCommandGroup注解,并放置在命令所在的包内。那么包中的命令都会分为一组。
这三种方法设置分组,优先级由高到低。
public class UserCommands {
@ShellCommand(value = "This command ends up in the 'User Commands' group")
public void foo() {}
@ShellCommand(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() {}
}
通过spring-shell-starter来创建的应用,都包含一组内置命令。help命令会展示当前系统可用命令。help 会展示命令的详细信息。
如果不想用内置命令,那么排除依赖即可。
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell-starter</artifactId>
<version>2.0.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell-standard-commands</artifactId>
</exclusion>
</exclusion>
</dependency>
如果只想禁用某个命令,那么可以将spring.shell.command..enabled 属性,并设置为false。在启动时,将参数传给系统。下面的demo禁用了help命令。
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
String[] disabledCommands = {"--spring.shell.command.help.enabled=false"};
String[] fullArgs = StringUtils.concatenateStringArrays(args,disabledCommands);
SpringApplication.run(DemoApplication.class, fullArgs);
}
}
如果想覆盖内置命令,那么实现.Command即可。
public class MyClear implements Clear.Command {
@ShellCommand("Clear the screen, only better.")
public void clear() {
// ...
}
}
Spring-Shell 基于JLine库提供了提供了REPL模式的控制台交互工具,我们可以通过自定义命令实现控制台命令功能。
[1].https://docs.spring.io/spring-shell/docs/2.0.0.RELEASE/reference/htmlsingle/
[2].https://github.com/spring-projects/spring-shell/issues/190