使用Spring Shell 快速开发自己的命令交互窗口 - 草稿

Spring Shell

有时候,为了方便开发和测试服务器,并不需要一个漂亮的用户界面,使用一个简单的命令窗口即可。如下所示:


交互式命令窗口

这里介绍一个快速,方便,易用,简单的交互式命令窗口开发组件-Spring Shell
没错,又是spring 生态中的。

源码地址

https://gitee.com/wgslucky/spring-shell-demo

创建项目

本项目是使用Eclipse作为开发的IDE,同样,直接导入到Idea之中也可以使用。使用的JDK需要是1.8或更高的版本,我测试过在JDK11上也可以使用。
在eclipse中创建maven项目:spring-shell-demo,然后在pom.xml中添加如下依赖:

   <parent>
       <!-- 添加spring boot 父pom依赖,这个不能少,spring shell官方的文档中没有写 -->
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.2.6.RELEASE</version>
   </parent>
   <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.shell</groupId>
           <artifactId>spring-shell-starter</artifactId>
           <version>2.0.0.RELEASE</version>
       </dependency>
   </dependencies>

添加启动类

@SpringBootApplication
public class SpringShellDemo {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(SpringShellDemo.class);
        app.setBannerMode(Mode.OFF);
        app.setWebApplicationType(WebApplicationType.NONE);
        app.run(args);
    }
}

到此,项目创建完成。下面就是见证奇迹的时候了。

快速添加一条命令

创建一个命令类,类名可以自定义:

@ShellComponent
public class MyCommand {
    @ShellMethod("连接服务器,格式:connect ip port")
    public String connect(String ip,int port) {
        return String.format("连接服务成功:%s:%s", ip,port);
    }
}

然后,运行项目的启动类main方法,在控制台就可以输入命令了:


输入命令

就是这么简单!!!,只需要两步:

  1. 在命令类上面添加注解:@ShellComponent
  2. 在方法上面添加注解:@ShellMethod

项目打包和运行

在项目的pom.xml所在的目录上执行

mvn clean package

然后在target的目录下面生成了运行包:spring-shell-demo-0.0.1-SNAPSHOT.jar
使用下面的命令可以直接运行此包:

java -jar spring-shell-demo-0.0.1-SNAPSHOT.jar

这样就可以在IDE外面随时使用了。

使用小技巧

现在已经可以轻松的添加自己想要的交互命令了,交互命令的最重要的职责就是接收用户输入的参数,剩下如何执行命令就是自己的业务逻辑了。下面介绍一些使用小技巧。

内置命令

Spring Shell内容了一些常用的命令,可以直接使用,可以在运行的命令窗口中输入help,快速查看这些命令:

shell:>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. 
        (显示异常栈,一般遇到错误时,使用它快速查看异常栈)

添加命令描述

可以在@ShellMethod注解中添加对此命令的描述,这样在使用help命令时,就可以看到这些描述,明白命令如何使用,如上面的第一个命令的例子,在命令窗口中输入help可以看到:

My Command
       connect: 连接服务器,格式:connect ip port

自定义命令的名字

默认情况下是不需要自定义命令的名字的,它会使用执行这个命令的方法名转化相应的窗口命令名字,如果是多个单词,使用-分开。比如connect方法,它的命令名字就是connect,如果方法名是sayHello,那么它的命令名字就是:say-hello
如果想要自定义命令名字,可以在@ShellMethod注解中添加:

    @ShellMethod(value = "登陆服务器,格式:login-server playerId",key = "login-server")
    public String login(String playerId) {
        return "登陆成功:" + playerId;
    }

修改提示符

在spring shell运行时,默认的命令提示符是:shell:> 有时候,看着别扭,如果能修改为自定义的提示符就好。这个可以修改,需要添加一个新的类:

@Service
public class CustomPromptProvider implements PromptProvider{

    @Override
    public AttributedString getPrompt() {
        return new AttributedString("xinyues-client:>");
    }

}

这样重新打包运行,输入的命令提示符就变成了 xinyues-client:>

带参数名输入命令参数

在输入命令时,如果不使用命令参数名字,那么参数的顺序必须和方法中定义的参数顺序一致。如果想使顺序不同,可以添加参数名称,如下所示:

xinyues-client:>connect --port 8888  --ip localhost
连接服务成功:localhost:8888
xinyues-client:>

参数需要两个横扛(--)。

添加默认参数值

有时候,一些命令可以简化一下,如果不输入参数的话,就使用参数的默认值。如下所示:

    @ShellMethod("连接服务器,格式:connect ip port")
    public String connectDefault(@ShellOption(defaultValue = 
"localhost")String ip,@ShellOption(defaultValue = "8080")int port) {
        return String.format("连接服务成功:%s:%s", ip,port);
    }

在参数上面使用了一个新的注解:@ShellOption
使用命令时可以直接输入命令名,而不需要输入参数:

xinyues-client:>connect-default
连接服务成功:localhost:8080
xinyues-client:>

使用数组参数

有时候,为了输入方便,不想定义太多的参数变量,或者参数是一个数组数据时,可以使用下面这种方式:

        @ShellMethod("Add Numbers.")
        public float add(@ShellOption(arity=3) float[] numbers) {
                return numbers[0] + numbers[1] + numbers[2];
        }

输入的参数带空格时

默认情况下,spring shell是以空格区分多个参数的,如果一个参数是多个单词,且有空格,就不能直接输入了,可以使用双引号或单引号,比如下面这个命令:

        @ShellMethod("Prints what has been entered.")
        public String echo(String what) {
                return "You said " + what;
        }

输入如下所示:

shell:>echo Hello
You said Hello
shell:>echo 'Hello'
You said Hello
shell:>echo 'Hello World'
You said Hello World
shell:>echo "Hello World"
You said Hello World

也可以这样使用,避免使用转义符:

shell:>echo "I'm here!"
You said I'm here!
shell:>echo 'He said "Hi!"'
You said He said "Hi!"

命令自动补全功能

可以使用tab键,自动补命令名字,和使用linux命令类似,也可以使用tab自动补全参数名字。

命令换行输入

有时候,命令参数太多,一行可能输入不完,可以在一行的末尾添加 \ ,然后另起一行输入命令:

shell:>register module --type source --name foo  \ 
> --uri file:///tmp/bar
Successfully registered module 'source:foo'

快捷键使用

  1. ctrl + r 搜索输入过历史执行过的命令。减少重复的输入,提高操作效率。
  2. ctrl + a 跳转到行头输入
  3. ctrl + e 跳转到行尾输入
  4. 上下箭头 输入完命令名字之后,按上下箭头可以查阅之前输入过的参数命令。

参数限制

可以使用注解,对参数进入限制,防止用户输入错误,如下所示:

        @ShellMethod("Change password.")
        public String changePassword(@Size(min = 8, max = 40) String password) {
                return "Password successfully set to " + password;
        }

如果输入的不符合要求,会有提示:

shell:>change-password hello
The following constraints were not met:
    --password string : size must be between 8 and 40 (You passed 'hello')

更多可以使用的注解,请参阅:https://beanvalidation.org/2.0/spec/#builtinconstraints

命令有效性检测

有时候,多个命令之间可能有某种依赖性,比如这样一个场景,客户端有一个下载download命令,但是在使用下载命令的时候,必须先connect成功。可以这样检测download命令是否可用:

@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() {
        [...]
    }
   // 注意,这里方法的命名是命令名 + Availability
    public Availability downloadAvailability() {
        return connected
            ? Availability.available()
            : Availability.unavailable("you are not connected");
    }
}

这样,如果用户没有使用connect命令,而直接使用download命令,就会提示:

xinyues-client:>download
�[31mCommand 'download' exists but is not currently available because you are not connected�[0m
�[31mDetails of the error have been omitted. You can use the �[1mstacktrace�[22m command to print the full stacktrace.�[0m
xinyues-client:>

上面这个检测方法是命名是有规则的必须是命令名 + Availability;另一种方式是使用注解指定检测的方法名:

    @ShellMethod("Download the nuclear codes.")
    @ShellMethodAvailability("availabilityCheck") 
    public void download() {
        [...]
    }

    public Availability availabilityCheck() { 
        return connected
            ? Availability.available()
            : Availability.unavailable("you are not connected");
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容