当前位置: 首页 > 工具软件 > Spring Shell > 使用案例 >

Spring Shell 中文文档

高山
2023-12-01

spring shell

官方链接

Version 2.0.0.RELEASE

可以参考这篇文章的视频简介: spring shell Java命令行集成

What is Spring Shell?

不是所有的应用程序都需要一个花哨的 web 用户界面!有时,使用交互式终端与应用程序交互是完成工作的最合适方式。


Spring Shell 允许您轻松创建这样一个可运行的应用程序,用户将在其中输入文本命令,这些命令将被执行,直到程序终止。 The Spring Shell project provides the infrastructure to create such a REPL (Read, Eval, Print Loop), 允许开发人员使用熟悉的 Spring 编程模型专注于命令实现。


高级功能,如解析、 TAB 自动补全、输出颜色化、奇特的 ascii-art 表显示、输入转换和验证,都免费提供, 开发人员只需关注核心命令逻辑。(Advanced features such as parsing, TAB completion, colorization of output, fancy ascii-art table display, input conversion and validation all come for free, with the developer only having to focus on core command logic.)


Using Spring Shell

Getting Started

为了了解 Spring Shell 提供了什么,让我们编写一个简单的 shell 应用程序,它有一个简单的命令将两个数字加在一起。

Let’s Write a Simple Boot App

从版本 2 开始,Spring Shell 已经从头开始重写,并考虑到各种增强功能,其中之一是易于与 Spring Boot 集成,尽管这不是一个强有力的要求。为了本教程的目的,让我们创建一个简单的引导应用程序,例如使用 start.spring.io。这个最小的应用程序只依赖于 spring-boot-starter,并配置 spring-boot-maven-plugin,生成一个可执行的 jar:

Adding a Dependency on Spring Shell

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

Your first command

是时候添加我们的第一个命令了。创建一个新类 (随心所欲地命名它),and annotate it with @ShellComponent (@Component的变体,用于限制扫描候选命令的类集).


然后,创建一个 add 方法,该方法接受两个整数 (a 和 b) 并返回它们的总和. Annotate it with @ShellMethod and 并在@ShellMethod 中提供命令的描述:

package com.example.demo;

import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellComponent;

@ShellComponent
public class MyCommands {

    @ShellMethod("Add two integers together.")
    public int add(int a, int b) {
        return a + b;
    }
}

Let’s Give It a Ride!

mvn clean install  -DskipTests
java -jar target/demo-0.0.1-SNAPSHOT.jar

您将会受到以下屏幕的欢迎 (横幅来自 Spring Boot,可以像往常一样定制):

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot

shell:>

下面是 shell:> 提示,输入 入 add 1 2 即可体验

shell:>add 1 2
3

尝试玩 shell (提示: 有一个help 命令),完成后,键入exit ENTER。
本文档的其余部分深入研究了整个 Spring Shell 编程模型。


Writing your own Commands

(spring 将方法转换为可执行的shell 是可插拔的)The way Spring Shell decides to turn a method into an actual shell command is entirely pluggable (see Extending Spring Shell), but as of Spring Shell 2.x, the recommended way to write commands is to use the new API described in this section (the so-called standard API).(但是从 Spring Shell 2.X 开始,编写命令的推荐方法是使用本节中描述的新 API (所谓的标准 API)。)

使用标准 API,beans 上的方法将被转换成可执行命令,前提是

  • the bean class bears the @ShellComponent annotation.这用于限制考虑的 bean 集。
  • the method bears the @ShellMethod annotation.

The @ShellComponent is a stereotype annotation itself meta-annotated with @Component. As such, it can be used in addition to the filtering mechanism to also declare beans (e.g. using @ComponentScan).

可以使用注释的 value 属性定制创建的 bean 的名称

It’s all about Documentation!

@ShellMethod 注释唯一需要的属性是它的 value 属性,该属性应该用于写一个简短的、一句话的命令操作描述。)(see Integrated Documentation with the help Command).

您的命令说明应该简短,只能是一两个句子。为了获得更好的一致性,建议以大写字母开头,以点结束。

Customizing the Command Name(s)

默认情况下,不需要为命令指定key (i.e. the word(s) that should be used to invoke it in the shell这个是在调用shell的时候被应用)。方法的名称将用作命令的key, turning camelCase names into dashed, gnu-style, names (that is, sayHello() will become say-hello). 名称默认请求使用方法的名称sayHello 转换为shell 就是 say-hello

可以使用注释的 key 属性显式设置命令键,如下所示:

@ShellMethod(value = "Add numbers.", key = "sum")
public int add(int a, int b) {
    return a + b;
}

Key 属性接受多个值。如果为单个方法设置多个键,则命令将使用这些不同的别名注册。


Invoking your Commands

By Name vs. Positional Parameters(按名称与位置参数)

如上所述,用 @ shellmethod 装饰方法是创建命令的唯一要求。这样做时,用户可以通过两种可能的方式设置所有方法参数的值:

  • using a parameter key (e.g. --arg value). 这种方法被称为 “按名称” 参数
  • or without a key, simply setting parameter values in the same order they appear in the method signature (“positional” parameters). 或者没有键,只需按照参数值在方法签名 (“位置” 参数) 中出现的顺序设置参数值。

这两种方法可以混合和匹配,命名参数总是优先 (因为它们不太容易模糊)。因此,给定以下命令

@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            //This uses positional parameters   
You said a=1, b=2, c=3

shell:>echo --a 1 --b 2 --c 3    //This is an example of full by-name parameters
You said a=1, b=2, c=3

shell:>echo --b 2 --c 3 --a 1   //By-name parameters can be reordered as desired
You said a=1, b=2, c=3

shell:>echo --a 1 2 3         //you can use a mix of the two approaches  
You said a=1, b=2, c=3

shell:>echo 1 --c 3 2       //	The non by-name parameters are resolved in the order they appear    
You said a=1, b=2, c=3

Customizing the Named Parameter Key(s) 自定义命名参数键

如上所述,导出命名参数的key的默认策略是使用方法签名的 java 名称,并用两个破折号 (–) 作为前缀。这可以通过两种方式定制:

  • 更改整个方法的默认前缀, use the prefix() attribute of the @ShellMethod annotation
  • 以每个参数的方式覆盖整个key , annotate the parameter with the @ShellOption annotation.
@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);
}

对于这种设置,可能的参数键将是-a 、-b 和 --third。

可以为一个参数指定几个键。如果是这样,这些将是指定相同参数的互斥方式 (因此只能使用其中一个) here is the signature of the built-in help command:

@ShellMethod("Describe a command.")
public String help(@ShellOption({"-C", "--command"}) String command) {
    ...
 }

Optional Parameters and Default Values (默认值)

Spring Shell 提供了给参数默认值的能力,这将允许用户省略这些参数:

@ShellMethod("Say hello.")
public String greet(@ShellOption(defaultValue="World") String who) {
    return "Hello " + who;
}

Parameter Arity(参数相关性)

到目前为止,一直假设每个参数映射到用户输入的单个单词。然而,当参数值应该是多值时,可能会出现这种情况。 This is driven by the arity() attribute of the @ShellOption annotation. 只需为参数类型使用集合或数组,并指定预期的值.

@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

Infinite Arity(无限度)

要执行

Special Handling of Boolean Parameters(布尔参数的特殊处理)

默认值为false

@ShellMethod("Terminate the system.")
public String shutdown(boolean force) {
  return "You said " + force;
} 

这允许以下调用:

shell:>shutdown
You said false
shell:>shutdown --force
You said true

具有隐式 arity()=0的这种行为阻止用户指定值 (e.g. shutdown --force true). If you would like to allow this behavior (and forego the flag approach), then force an arity of 1 using the annotation:

@ShellMethod("Terminate the system.")
public String shutdown(@ShellOption(arity=1, defaultValue="false") boolean force) {
    return "You said " + force;
}

Quotes Handling(引用处理)

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

如果用户需要嵌入用于引用整个参数的相同类型的引用,转义序列将使用反斜杠 () 字符:

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

Interacting with the Shell

The Spring Shell project builds on top of the JLine library,因此,带来了许多不错的互动功能,其中一些在本节中详细介绍。

首先,Spring Shell 几乎在任何可能的地方都支持 TAB 完成。因此,如果有一个 echo 命令,用户按下 e,c,TAB,echo 就会出现。如果有几个命令以 ec 开头,则系统会提示用户选择 (使用 TAB 或 Shift + TAB 导航,并输入进行选择。))

除了命令之外还支持参数. It also works for parameter keys (--arg) and even parameter values, if the application developer registered the appropriate beans (see Providing TAB Completion Proposals).

Spring Shell 应用程序的另一个很好的功能是支持线路延续。如果命令及其参数太长,并且在屏幕上不太合适,用户可以用反斜杠 () 块它并终止一行字符然后点击 ENTER 并继续下一行。Uppon 提交整个命令,这将被解析,就像用户在换行符上输入了一个空格一样。

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

最后,Spring Shell 应用程序受益于许多键盘快捷键,在使用从 Emacs 借来的常规操作系统 Shell 时,您可能已经熟悉了这些快捷键。值得注意的快捷方式包括 Ctrl + r 执行反向搜索,Ctrl + a 和 Ctrl + e 分别移动到行的开始和结束,或者 Esc f 和 Esc b 向前移动 (resp。向后) 一个单词一次。

Providing TAB Completion Proposals

Validating Command Arguments (验证命令参数)

Spring Shell 与 Bean 验证 API 集成,以支持对命令参数的自动和自记录约束。

命令参数上的注释以及方法级别的注释将被遵守,并在命令执行之前触发验证。给出以下命令:

@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')

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

由于应用程序的内部状态,注册的命令有时可能没有意义。例如,也许有一个下载命令,但是只有当用户在远程服务器上使用 connect 后,它才会工作。现在,如果用户尝试使用下载命令,shell 应该优雅地解释该命令确实存在,但是它当时不可用。Spring Shell 允许开发人员这样做,甚至对命令不可用的原因提供了简短的解释。

命令有三种可能的方式来表示可用性。他们都利用了返回可用性实例的无 arg 方法。让我们从一个简单的例子开始:

@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() {
        [...]
    }
    public Availability downloadAvailability() {
        return connected
            ? Availability.available()
            : Availability.unavailable("you are not connected");
    }
}

其名称中有可用性后缀。该方法返回可用性实例,使用两种工厂方法之一构建.download+Availability

集成帮助中也利用了有关当前不可用命令的信息。请参见帮助命令的集成文档。 Integrated Documentation with the help Command.

如果由于某种原因,以命令方法的名称命名可用性方法不适合您,您可以使用 @ShellMethodAvailability可用性提供显式名称,如下所示:

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

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

Lastly, it is often the case that several commands in the same class share the same internal state and thus should all be available or unavailable all at one. Instead of having to stick the @ShellMethodAvailability on all command methods, Spring Shell allows the user to flip things around and put the @ShellMethodAvailabilty annotation on the availability method, specifying the names of the commands that it controls:

最后,通常情况下,同一个类中的几个命令共享相同的内部状态,因此都应该同时可用或不可用。Spring Shell 不需要在所有命令方法上粘上 @ShellMethodAvailability,而是允许用户翻转东西,并在可用性方法上放置 @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");
}

Organizing Commands (命令分组)

当你的 shell 开始提供很多功能时,你可能会得到很多命令,这可能会让你的用户感到困惑。键入 help 他们会看到一系列令人生畏的命令,按字母顺序排列,这可能并不总是有意义的。

为了缓解这种情况,Spring Shell 提供了将命令组合在一起的能力,并提供了合理的默认值。然后,相关命令将最终在同一个组中 (例如用户管理命令),并一起显示在帮助屏幕和其他地方。

默认情况下,命令将根据它们实现的类分组,将骆驼案例类名称转换成单独的单词。

但是,如果此行为不适合您,您可以按照优先级顺序,以以下方式覆盖命令的组:

  • specifying a group() in the @ShellMethod annotation
  • placing a @ShellCommandGroup on the class the command is defined in. This will apply the group for all commands defined in that class (unless overridden as above)
  • placing a @ShellCommandGroup on the package (via package-info.java) the command is defined in. This will apply to all commands defined in the package (unless overridden at the method or class level as explained above)
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() {}
}

Built-In Commands(内置命令)

都附带一组内置命令。这些命令可以单独被覆盖或禁用 (see Overriding or Disabling Built-In Commands)

ntegrated Documentation with the help Command

运行 shell 应用程序通常意味着用户处于图形有限的环境中。尽管在手机时代,我们总是连接在一起,但访问 web 浏览器或任何其他富 UI 应用程序 (如 pdf viewer) 可能并不总是可能的。这就是为什么正确地自我记录 shell 命令很重要,这就是 help 命令的来源。



键入 help + ENTER 将向 shell 列出所有已知命令 (包括不可用命令),并简要描述它们的工作:

shell:>help
AVAILABLE COMMANDS
        add: Add numbers together.
      * authenticate: Authenticate with the system.
      * blow-up: Blow Everything up.
        clear: Clear the shell screen.
        connect: Connect to the system
        disconnect: Disconnect from the system.
        exit, quit: Exit the shell.
        help: Display help about available commands.
        register module: Register a new module.
        script: Read and execute commands from a file.
        stacktrace: Display the full stacktrace of the last error.
Commands marked with (*) are currently unavailable.
Type `help <command>` to learn more.

键入 help 将显示有关命令的更详细信息,包括可用参数、它们的类型以及它们是否是强制性的,等等。

shell:>help help


NAME
	help - Display help about available commands.

SYNOPSYS
	help [[-C] string]

OPTIONS
	-C or --command  string
		The command to obtain help for.  [Optional, default = <none>]

Clearing the Screen

Clear 命令执行您期望的操作,并清除屏幕,重置左上角的提示。

Exitting the Shell

退出命令quit (也别名为exit) 只需请求 shell 退出,优雅地关闭 Spring 应用程序上下文。如果没有被覆盖,JLine 历史记录 bean 将编写所有执行到磁盘的命令的历史记录.

Displaying Details about an Error

当命令代码中出现异常时,它被 shell 捕获,并显示一条简单的单行消息,以免过多的信息溢出用户。尽管在某些情况下,了解到底发生了什么很重要 (尤其是如果异常有嵌套原因)。
为此,Spring Shell 记得最后一次发生的异常,用户以后可以使用 stacktrace 命令在控制台上打印所有血淋淋的详细信息。

Running a Batch of Commands(运行一批命令)

The script命令接受本地文件作为参数,并将一次重放在那里找到的命令。
从文件中读取的行为与交互式 shell 中的行为完全一样,因此以//开头的行将被视为注释并被忽略,而以 \ 结尾的行将触发行继续。


Customizing the Shell

Overriding or Disabling Built-In Commands

Spring Shell 提供了内置命令,以实现许多 (如果不是所有的 shell 应用程序) 需要的日常任务。如果您对它们的行为方式不满意,您可以禁用或覆盖它们,如本节所述。

Disabling all Built-in Commands
如果你根本不需要内置命令,那么有一个简单的方法来 “禁用” 它们: 只是不包括它们!要么在spring-shell-standard-commands使用 maven 排除,要么,如果你有选择地包括 Spring Shell 依赖项,不要引入它!

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

Disabling Specific Commands

要禁用单个内置命令,只需在应用程序环境中将 spring.shell.command..Enable 属性设置为 false。这样做的一个简单方法是在 main () 入口点向引导应用程序传递额外的参数:

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

Overriding Specific Commands

如果您不希望禁用命令,而是希望提供自己的实现,那么您可以

  • 禁用上面解释的命令,并使用相同的名称注册您的实现
  • have your implementing class implement the .Command interface.
public class MyClear implements Clear.Command {

    @ShellCommand("Clear the screen, only better.")
    public void clear() {
        // ...
    }
}

ResultHandlers

PromptProvider

每次命令调用后,shell 都会等待用户的新输入,显示黄色提示:

可以通过注册类型为 PromptProvider 的 bean 来定制这种行为。这样的 bean 可以使用内部状态来决定向用户显示什么 (例如,它可以对应用程序事件做出反应),并且可以使用 JLine 的属性字符序列来显示花哨的 ANSI 文本。

@Component
public class CustomPromptProvider implements PromptProvider {

        private ConnectionDetails connection;

        @Override
        public AttributedString getPrompt() {
                if (connection != null) {
                        return new AttributedString(connection.getHost() + ":>",
                                AttributedStyle.DEFAULT.foreground(AttributedStyle.YELLOW));
                }
                else {
                        return new AttributedString("server-unknown:>",
                                AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
                }
        }

        @EventListener
        public void handle(ConnectionUpdatedEvent event) {
                this.connection = event.getConnectionDetails();
        }
}

Customizing Command Line Options Behavior(自定义命令行选项行为)

Spring Shell 附带两个默认的 Spring Boot 应用程序:

  • InteractiveShellApplicationRunner bootstraps the Shell REPL. It sets up the JLine infrastructure and eventually calls Shell.run()
  • ScriptShellApplicationRunner looks for program arguments that start with @, assumes those are local file names and tries to run commands contained in those files (with the same semantics as the script command) and then exits the process (by effectively disabling the InteractiveShellApplicationRunner, see below).

如果这种行为不适合您,只需提供一个 (或多个) application ationrunner 类型的 bean,并可选地禁用标准 bean。

@Order(InteractiveShellApplicationRunner.PRECEDENCE - 100) // Runs before InteractiveShellApplicationRunner
public class ScriptShellApplicationRunner implements ApplicationRunner {

        @Override
        public void run(ApplicationArguments args) throws Exception {
                List<File> scriptsToRun = args.getNonOptionArgs().stream()
                                .filter(s -> s.startsWith("@"))
                                .map(s -> new File(s.substring(1)))
                                .collect(Collectors.toList());

                boolean batchEnabled = environment.getProperty(SPRING_SHELL_SCRIPT_ENABLED, boolean.class, true);

                if (!scriptsToRun.isEmpty() && batchEnabled) {
                        InteractiveShellApplicationRunner.disable(environment);
                        for (File file : scriptsToRun) {
                                try (Reader reader = new FileReader(file);
                                                FileInputProvider inputProvider = new FileInputProvider(reader, parser)) {
                                        shell.run(inputProvider);
                                }
                        }
                }
        }

Customizing Arguments Conversion(自定义参数转换)

从文本输入到实际方法参数的转换使用标准的 Spring 转换机制。 Spring Shell installs a new DefaultConversionService (with built-in converters enabled) and registers to it any bean of type Converter<S, T>, GenericConverter or ConverterFactory<S, T> that it finds in the application context.


这意味着定制到 Foo 类型的自定义对象的转换非常容易: just install a Converter<String, Foo> bean in the context.

@ShellComponent
class ConversionCommands {

        @ShellMethod("Shows conversion using Spring converter")
        public String conversionExample(DomainObject object) {
                return object.getClass();
        }

}

class DomainObject {
        private final String value;

        DomainObject(String value) {
                this.value = value;
        }

        public String toString() {
                return value;
        }
}

@Component
class CustomDomainConverter implements Converter<String, DomainObject> {

        @Override
        public DomainObject convert(String source) {
                return new DomainObject(source);
        }
}

 类似资料: