springshell实践

springshell是spring提供的一个组件,版本也已对springboot环境提供集成,便于整合其他组件,通过启动服务提供命令行式服务.

1.pom引入,搭建基础springboot(maven)工程

        <!-- springshell包及基础依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.6.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.shell</groupId>
            <artifactId>spring-shell-starter</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
            <version>1.18.24</version>
        </dependency>

2.编写启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringShellDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringShellDemoApplication.class, args);
    }

}

在idea中启动工程,可在控制台输入命令, 或者maven打包命令行运行(在对应目录下):

mvn clean install  -DskipTests
java -jar target/***.jar

3.命令操作

package com.example.springshelldemo.command;

import org.springframework.shell.standard.ShellCommandGroup;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

// @ShellComponent 扫描命令的类集
@ShellComponent
@ShellCommandGroup(value = "SpringShellDemo Commands") // 不添加则默认为@ShellCommandGroup(value="驼峰类名以空格分隔")
public class MyCommands {
    // @ShellMethod 扫描命令,Value值英文一般首字母大写,以"."结尾. 如使用中文方法注解,注意变更Linux环境的字符集,value为方法描述,key为命令行方法名称
    @ShellMethod(value = "Add two nums together.", key = "sum")
    public int add(int a, int b){
        return a+b;
    }

}

注解 

@ShellComponent 扫描命令的类集

@ShellCommandGroup(value = "")  不添加则默认为@ShellCommandGroup(value="驼峰类名以空格分隔"),用于命令分组;

@ShellMethod(value  = "", key = "")  扫描动作命令,value值英文一般首字母大写,以"."结尾. 如使用中文方法注解,注意变更Linux环境的字符集,value为方法描述,key为命令行方法名称;

命令行参数可以按位置或者按照名称匹配;其中按位置如: echo 1 2 3 ;按名称默认为 --arg 如: echo --a --b --c ,亦可混合使用;

    // 命令行参数可以按位置或者按照名称匹配;其中按位置如: echo 1 2 3 ;按名称默认为 --arg 如: echo --a --b --c ,亦可混合使用;
    @ShellMethod(value = "Dispaly stuff.")
    public String echo(String a, String b, String c){
        return String.format("Param is a=%d ,b=%d, c=%d", a, b, c);
    }

操作:

shell:>echo a b paramC
You said a=a, b=b, c=paramC
shell:>echo --a a --b b --c paramC
You said a=a, b=b, c=paramC
shell:>echo a --b b paramC
You said a=a, b=b, c=paramC

4.自定义参数命名键

更改整个方法的默认前缀, use the prefix() attribute of the @ShellMethod annotation

以每个参数的方式覆盖整个key , annotate the parameter with the @ShellOption annotation.

    // @ShellMethod prefix 设置整个方法的默认前缀
    @ShellMethod(value = "Display stuff.", prefix = "-", key = "echoc")
    // @ShellOption 设置具体某个参数的名称导入方式.如: -c 或者 --third 名称匹配参数c;
    public String echoCustomizing(String a, String b, @ShellOption(value = {"-c", "--third"}) String c) {
        return String.format("You said a=%s, b=%s, c=%s", a, b, c);
    }

操作:

shell:>echoc a b -c paramC
You said a=a, b=b, c=paramC
shell:>echoc -a a -b b --third paramC
You said a=a, b=b, c=paramC

5.默认值

@ShellOption对参数进行设置,defaultValue 默认值, help设置help提示(如 help query)

@ShellMethod("query appName.")
    // @ShellOption对参数进行设置,defaultValue 默认值, help设置help提示(如 help query)
    public String query(@ShellOption(defaultValue = "defaultAppName", help = "应用名称") String appName){ return appName; }

操作:

shell:>query
defaultAppName
shell:>help query


NAME
    query - query appName.

SYNOPSYS
    query [[--app-name] string]  

OPTIONS
    --app-name  string
        应用名称
        [Optional, default = defaultAppName]

 6.参数相关性arity

@ShellOption arity 参数相关性,参数类型为数组或集合,并指定预期的值;

@ShellMethod(value = "add Array nums.", key = {"add-array", "addarray"})
// @ShellOption arity 参数相关性,参数类型为数组或集合,并指定预期的值;
public int addArray(@ShellOption(arity = 3) int[] nums) {
return nums[0] + nums[1] + nums[2];
}

操作:

shell:>add-array 1 2 3
6

7.布尔类型参数的处理

boolean 默认为@ShellOption arity = 0,即默认为false, --arg 则为true;

    @ShellMethod(value = "Terminate the system.")
    // boolean 默认为@ShellOption arity = 0,即默认为false, --arg 则为true;
    public String shutdown(boolean force) {
        return "The system terminate " + force;
    }

操作:

shell:>shutdown
The system terminate false
shell:>shutdown --force
The system terminate true

boolean 亦可设置为@ShellOption arity = 1,defaultValu为false;

    @ShellMethod(value = "Terminate the system.", key = {"shutdown-system", "shutdownsystem"})
    // boolean 亦可设置为@ShellOption arity = 1,defaultValu为false;
    public String shutdownSystem(@ShellOption(arity = 1, defaultValue = "false") boolean force) {
        return "The system terminate " + force;
    }

操作:

shell:>shutdownsystem
The system terminate false
shell:>shutdownsystem true
The system terminate true

 8.引用处理(quotes handling)

默认使用空格分隔参数,包含特殊字符的参数,可使用单引号('')或者双引号("")来确定参数,转义使用反斜杠(\);

使用上面的query方法示例操作:

shell:>query hello
hello
shell:>query 'hello world'
hello world
shell:>query "hello world"
hello world
shell:>query 'I\'m here!'
I'm here!
shell:>query "He said \"Hi!\""
He said "Hi!"

springshell支持TAB快捷操作,参数太长,可使用反斜杠("\")换行输入;

 

9.参数校验(Validating Command Arguments)

    @ShellMethod("checklength.") // javax.validation.constraints.Size
    public String checklength(@Size(min = 2, max = 10) String username){
        return "the username is " + username;
    }

操作:

shell:>checklength a
The following constraints were not met:
    --username string : 个数必须在2和10之间 (You passed 'a')
shell:>checklength yaoming
the username is yaoming
shell:>checklength yaoming123456789
The following constraints were not met:
    --username string : 个数必须在2和10之间 (You passed 'yaoming123456789')

10.动态命令可用性(Dynamic Command Availability)

springshell允许判断命令是否可用,并优雅提示;

@ShellMethodAvailability(value  = "") 扫描动作命令;

@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() {
        [...]
    }

@ShellMethodAvailability({"download", "disconnect"}) public 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:>disconnect
No command found for 'disconnect'
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.
shell:>connect admin admin
shell:>download

11.命令分组(Organizing Commands)

使用@ShellMethod group 进行分组展示;

内置命令(Built-In Commands)

命令行键入 help + ENTER 即可输出所有命令,其中内置命令如下:

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.

clear: 清空屏幕

exit/quit: 退出

help: 显示可用命令的帮助信息

script: 从文件中读取并执行命令

stackstrace: 显示最后一个错误的完整堆栈跟踪

如不需要,可禁用内置命令,禁用全部内置命令:

<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>

禁用某一条:

    public static void main(String[] args) {
     String[] disabledCommands = {
       "--spring.shell.command.exit.enabled=false",
       "--spring.shell.command.quit.enabled=false"}; String[] fullArgs = StringUtils.concatenateStringArrays(args, disabledCommands); SpringApplication.run(SpringShellDemoApplication.class, fullArgs); }

并可在禁用后提供自定义实现:

package com.example.springshelldemo.command;

import org.springframework.shell.ExitRequest;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.commands.Quit;

@ShellComponent
public class ExitCommand implements Quit.Command {
    public ExitCommand() {
    }

    @ShellMethod(
            value = "Exit the shell.",
            key = {"quit", "exit"}, group = "SpringShellDemo Commands"
    )
    public void quit() {
        throw new ExitRequest();
    }

    public interface Command {
    }

}

 12.自定义内置命令提示符(ResultHandlers)

import lombok.extern.slf4j.Slf4j;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStyle;
import org.springframework.shell.jline.PromptProvider;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.net.UnknownHostException;

@Slf4j
@Component
public class CustomPromptProvider implements PromptProvider {

    @Override
    public AttributedString getPrompt() {
        // 获取主机名称
        String hostName = getHostName();
        // 设置命令提示符文字
        String promot = "SpringShellDemo@" + hostName + "> ";
        // 设置命令提示符字体样式
        AttributedStyle promotStyle = AttributedStyle.BOLD.foreground(AttributedStyle.GREEN);
        // 返回命令提示符
        return new AttributedString(promot, promotStyle);
    }


    /**
     * @Description: 获取主机名称
     * @return: String 主机名称
     * @author: zongf
     * @time: 2019-01-26 08:58:45
     */
    private String getHostName(){
        String hostName = "";
        try {
            InetAddress inetAddress = InetAddress.getLocalHost();
            hostName = inetAddress.getHostName();
        } catch (UnknownHostException e) {
            log.info("获取主机名称 UnknownHostException: {}", e.getMessage());
            e.printStackTrace();
            return "unknowHost";
        }
        return hostName;
    }
}

操作:

SpringShellDemo@LAPTOP-JSP5MRO1> 

启动banner图案可参考springboot banner生成banner文件即可;

13.启动日志屏蔽,可在配置文件中添加:

#设置整个项目日志级别
logging.lever.root=warn
#设置操作包目录com.example.springshelldemo.command日志级别
logging.lever.com.example.springshelldemo.command=info

 

posted @ 2022-05-24 00:23  刀霸汉  阅读(1421)  评论(0编辑  收藏  举报