欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Boot 整合——使用Spring Shell开发java命令行工具

程序员文章站 2022-05-01 16:59:01
...

关于版本

依赖 版本
springboot 2.0.8.RELEASE
spring-shell-starter 2.0.0.RELEASE

关于项目

本内容也是我尝试整理工作中接触过各种工具在springboot中使用的方法。下面介绍的所有方法都已经提供了测试用例。因为每个例子涉及代码较多,所以文章中只贴出了部分代码。全部的代码在这里gitee代码仓库

spring shell

Spring-shell是Spring提供的一个组件,此组件可以将Java中的代码逻辑封装为shell命令。通过启动服务器上的shell服务来通过命令方式执行java代码逻辑

简单的demo

依赖

    <dependency>
        <groupId>org.springframework.shell</groupId>
        <artifactId>spring-shell-starter</artifactId>
        <version>2.0.0.RELEASE</version>
    </dependency>

声明shell命令

将java逻辑转换为命令需要@ShellComponent指定类为命令行组件、以及使用@ShellMethod指定方法为命令方法

/**
 * 基础命令
 */
@ShellComponent
public class BaseCommands {

    /**
     * 基础的命令
     * @param a
     * @param b
     * 输入:add 2 3
     * 输出:5
     * @return
     */
    @ShellMethod("输入两个整数,获取相加结果")
    public int add(int a, int b) {
        return a + b;
    }

}

启动服务并执行命令

服务启动后界面会出现shell:>,默认情况下方法名称即使命令名称,针对大小写的名称比如(getName)会将大写字母进行处理为:get-name

自定义命令名称

spring-shell默认使用方法名称作为命令的名称,我们可以在方法的注解中声明key属性来重命名其命令名称

    /**
     * 指定命令名称,此时不能通过add2访问命令
     * 默认情况下,方法名称即使命令名称,但是通过指定key值来表示命令的名称
     * 输入:sum 3 5
     * 输出:8
     * @return
     */
    @ShellMethod(value = "输入两个整数,获取相加结果", key = "sum")
    public int add2(int a, int b) {
        return a + b;
    }

创建不同参数的命令

spring-shell支持数字、字符串、数组类型的参数

@ShellComponent
public class ArgCommands {

    /**
     * 多参数 可以使用 --arg value 指定参数名称
     * 输入:echo-int --b 1 --a 2 --c 3
     * 输出:You said a=2, b=1, c=c
     *
     * 输入:echo-int 1 2 3
     * 输出:You said a=1, b=2, c=3
     * @return
     */
    @ShellMethod("通过明明参数名称,来指定输入的数据对应的参数名称")
    public String echoInt(int a, int b, int c) {
        return String.format("You said a=%d, b=%d, c=%d", a, b, c);
    }


    /**
     *
     * 输入:echo-int2 1 2  3
     * 输出:You said a=1, b=2, c=3
     *
     * 输入:echo-int2 -b 2 -a 3 --third 4
     * 输出:You said a=3, b=2, c=4
     * @return
     */
    @ShellMethod(value = "通过明明参数名称,强制的指定输入的数据对应的参数名称", prefix="-")
    public String echoInt2(int a, int b, @ShellOption("--third") int c) {
        return String.format("You said a=%d, b=%d, c=%d", a, b, c);
    }

    /**
     * 设置默认值
     * 输入:echo-string --who ' string is "name"'
     * 输出:input: string is "name"
     * @return
     */
    @ShellMethod("输入字符串")
    public String echoString(@ShellOption(defaultValue="World") String who) {
        return "input:" + who;
    }

    /**
     * 数组类参数
     * 输入:echo-array 2 3 4
     * 输出:input:2.0,3.0,4.0
     * @return
     */
    @ShellMethod("输入数组")
    public String echoArray(@ShellOption(arity=3) float[] numbers) {
        return "input:" + numbers[0] + "," + numbers[1] + "," + numbers[2];
    }

    /**
     * boolean类型参数,boolean 类型参数当你设置了参数会返回true
     * 输入:echo-boolean --force
     * 输出:input:true
     *
     * 输入:echo-boolean
     * 输出:input:false
     * @return
     */
    @ShellMethod("Terminate the system.")
    public String echoBoolean(boolean force) {
        return "input:" + force;
    }

}

针对参数的使用,spring-shell遵循shell参数输入的格式。

多参数命令的使用

对于多参数的方法,我们可以使用参数顺序来和参数进行一一对应也可以使用--argname来对应命令中对具体参数名称。

比如在echoInt2方法中使用下面两种输入方法都是可以转型的

echo-int2 1 2  3
You said a=1, b=2, c=3

echo-int2 -b 2 -a 3 --third 4
You said a=3, b=2, c=4

匹配参数的--前缀并非是固定的,可以通过在注解中添加prefix="-"来调整前缀内容。

进行校验的命令

关于命令的校验,主要有两种

针对参数的校验

spring-shell提供了对命令中输入的值内容的校验,下面例子中对输入的值就有了长度的要求。

    @ShellMethod("只能输入长度为8至40的内容")
    public String changePassword(@Size(min = 8, max = 40) String password) {
        return "Password successfully set to " + password;
    }

spring-shell使用javax.validation.constraints包中的注解进行校验。具体可以查看此包中的内容来尝试对命令内容进行限制。

针对整体状态的校验

spring-shell使用Availability提供了另外一种校验。

在调用某些命令的时候我们需要保证系统环境中有些状态是符合要求的,否则这个命令是禁止调用的,当然可以在方法中通过if来进行手动判断,但是spring-shell提供了另外一个方式

    private boolean connected;

    @ShellMethod("设置链接状态为true")
    public void connect() {
        connected = true;
    }

    /**
     * 输入:download
     * 输出:
     * Command 'download' exists but is not currently available because 没有进行链接
     * Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.
     *
     * 第二次输入
     * 输入:>connect
     * 输出:>download
     */
    @ShellMethod("必须链接后才能执行的方法")
    public void download() {
    }

    public Availability downloadAvailability() {
        return connected
                ? Availability.available()
                : Availability.unavailable("没有进行链接");
    }

connected没有连接的时候调用download命令是不合法的。所以在每次调用download命令时会执行命令名称+Availability的方法,在这个方法中可以根据执行结果输出对应的错误信息。

自定义校验方法

除了使用默认的规则匹配方法,我们还可以主动去指定校验方法,使用ShellMethodAvailability注解我们可以指定此命令的校验方法

    /**
     * 为命令指定校验方法
     */
    @ShellMethod("必须链接2链接后才能执行的方法")
    @ShellMethodAvailability("availabilityCheck")
    public void disconnect2() {
    }

为校验指定需要限制的命令

除了为命令指定校验、也可以为校验指定需要限制的命令。当有多个命令需要接受同一种限制的时候,无需在所有方法中都添加相关注解,而是可以直接在校验方法中补充注解关联命令

    /**
     * 为校验方法指定需要校验的命令
     * @return
     */
    @ShellMethodAvailability({"download2", "disconnect2"})
    public Availability availabilityCheck() {
        return connected2
                ? Availability.available()
                : Availability.unavailable("没有进行链接");
    }

为命令提供分组

help是spring-shell的一个内置命令,使用此命令可以查看spring-shell中所有存在的命令。如果我们的命令比较多的时候此时列出来的会是一个长长的清单,这个时候可以使用注释中的group属性对命令进行分组

/**
 * 为命令分组
 */
@ShellComponent
@ShellCommandGroup("分组的命令")
public class GroupCommands {

    @ShellMethod("命令1")
    public void download1() {
    }

    @ShellMethod("命令2")
    public void download2() {
    }

    @ShellMethod("命令3")
    public void download3() {
    }


    @ShellMethod(value = "命令4",group = "其他组")
    public void download4() {
    }

}

使用help查看的内容为:

其他组
        download4: 命令4

分组的命令
        download1: 命令1
        download2: 命令2
        download3: 命令3

自定义命令行颜色和链接信息

通过修改PromptProvider中的内容,可以修改shell等待时的内容和颜色.下面内容中通过设置名称和颜色来调整shell等待时的内容。

@Component
public class CustomPromptProvider implements PromptProvider {

    @Override
    public AttributedString getPrompt() {
        return new AttributedString("my-shell:>",
                AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
    }

}

内置命令

通过help可以查看spring-shell所有内置命令

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.

help:主要是查看spring-shell所有命令

clear:将清除屏幕

quit:(也别名为exit)会退出spring-shell并关闭关闭Spring应用程序上下文

script:允许指定一个本地文件来进行批命令处理

实现更多的功能

使用spirng-shell我们可以将一些java中的逻辑封装为shell命令。当然有些操作我们可以编写shell脚本来实现,但是使用spring-shell提供了这些需求的另外一种可能

查看当前时间

    @ShellMethod("查询当前时间")
    public String date() {
        LocalDateTime dateTime = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return dateTime.format(formatter);
    }

克隆git代码

    @ShellMethod("获取指定仓库的分支")
    public String getBranch(String url){
        StringBuffer stringBuffer = new StringBuffer();
        try {
            Collection<Ref> refList = Git.lsRemoteRepository().setRemote(url).call();
            List<String> branchnameList = new ArrayList<>(4);
            for (Ref ref : refList) {
                String refName = ref.getName();
                if (refName.startsWith("refs/heads/")) {                       //须要进行筛选
                    String branchName = refName.replace("refs/heads/", "");
                    branchnameList.add(branchName);
                }
            }
            stringBuffer.append("共有分支" + branchnameList.size() + "个。");
            for (String item : branchnameList) {
                stringBuffer.append("[").append(item).append("]");
            }
        } catch (Exception e) {
            return "error";
        }
        return stringBuffer.toString();
    }

输出内容

shell:>get-branch https://gitee.com/daifyutils/springboot-samples.git 
共有分支1个。[master]

查看指定文件

    @ShellMethod("读取指定文件夹内容")
    public String cloneGit(String url){
        //克隆代码库命令
        CloneCommand cloneCommand = Git.cloneRepository();

        Git git= null;
        try {
            git = cloneCommand.setURI(url) //设置远程URI
                    .setBranch("master") //设置clone下来的分支
                    .setDirectory(new File("D:\\shell-git")) //设置下载存放路径
                    .call();
        } catch (GitAPIException e) {
            return "error";
        }
        return "finish";
    }

个人水平有限,上面的内容可能存在没有描述清楚或者错误的地方,假如开发同学发现了,请及时告知,我会第一时间修改相关内容。假如我的这篇内容对你有任何帮助的话,麻烦给我点一个赞。你的点赞就是我前进的动力。