-
Notifications
You must be signed in to change notification settings - Fork 392
v4 migration guide
This guide aims at helping you migrate your Spring Shell applications from v3 to v4. It covers the main breaking changes and deprecations introduced in Spring Shell v4.
Spring Shell 4 is based on Spring Framework 7. Spring Boot support in Spring Shell now requires Spring Boot 4+. Please make sure to upgrade your dependencies accordingly.
- The
spring-shell-coremodule does NOT depend on Spring Boot anymore. If you are using Spring Shell in a Spring Boot application, please add an explicit dependency tospring-shell-core-autoconfigureor one of the Spring Shell starters. - The
spring-shell-coremodule does NOT depend on JLine anymore. By default, the core module uses the system'sjava.io.Consoledefined in the JDK. If you want to use the JLine console, please add an explicit dependency tospring-shell-jline. - The modules
spring-shell-standardandspring-shell-standard-commandshave been merged in thespring-shell-coremodule. You do not need to add any explicit dependency to these modules anymore. - The module
spring-shell-tablehas been merged inspring-shell-jline. You do not need to add any explicit dependency to this module anymore if you are using JLine. - The module
spring-shell-autoconfigurehas been renamed tospring-shell-core-autoconfigure.
The legacy annotations @ShellComponent, @ShellMethod, @ShellOption and related annotations that were deprecated in v3 have been removed in v4. Please use the new annotations @Command, @Option, @Argument and other annotations defined in org.springframework.shell.core.command.annotation of the spring-shell-core module.
It is not possible to use the @Command annotation on a top-level class anymore. The following confusing (!) usage is no longer supported:
@Command
class Example {
@Command(command = "example")
public String example() {
return "Hello";
}
}Moreover, the attribute command of the @Command annotation has been renamed to name for better clarity.
However, annotated methods should still be defined in a Spring-managed bean (e.g., @Component, @Service, etc.).
In Spring Shell v4, it is recommended to define (related) commands in dedicated classes. For example:
@Component
public class GitHubCommands {
@Command(name = { "github", "auth", "login" }, description = "Login to GitHub", group = "github")
public void githubLogin(CommandContext commandContext) {
PrintWriter writer = commandContext.outputWriter();
writer.println("Logging in to GitHub...");
writer.flush();
}
@Command(name = { "github", "auth", "logout" }, description = "Logout from GitHub", group = "github")
public void githubLogout(CommandContext commandContext) {
PrintWriter writer = commandContext.outputWriter();
writer.println("Logging out from GitHub...");
writer.flush();
}
}In Spring Shell 3, it was required to use @EnableCommand or @CommandScan even when using Spring Boot. This is NOT the opinionated Boot way of discovering/doing things, and is not consistent with the rest of the portfolio.
In Spring Shell 4, if you are using Spring Boot, command scanning is automatically enabled (and therefore, @CommandScan was removed). You do not need to add any explicit annotation to your configuration anymore. For example:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.shell.core.command.annotation.Option;
@SpringBootApplication
public class SpringShellApplication {
public static void main(String[] args) {
SpringApplication.run(SpringShellApplication.class, args);
}
@Command
public String hello(@Option(defaultValue = "World") String name) {
return "Hello " + name + "!";
}
}However, if you are not using Spring Boot, you still need to use @EnableCommand to enable command discovery. For example:
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.shell.core.ShellRunner;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.shell.core.command.annotation.EnableCommand;
import org.springframework.shell.core.command.annotation.Option;
@EnableCommand(SpringShellApplication.class)
public class SpringShellApplication {
public static void main(String[] args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringShellApplication.class);
ShellRunner runner = context.getBean(ShellRunner.class);
runner.run(args);
}
@Command
public String hello(@Option(defaultValue = "World") String name) {
return "Hello " + name + "!";
}
}The main issues related to this change are https://github.com/spring-projects/spring-shell/issues/1158 and https://github.com/spring-projects/spring-shell/issues/1206.
In Spring Shell 3, customizing commands required a number of different annotations, which was confusing to many users. In Spring Shell 4, all command-related customizations are done via the @Command annotation and its attributes.
For example, customizing command availability can be done via a single annotation instead of two annotations:
// v3
@Command(name = "download")
@CommandAvailability("loginAvailabilityProvider")
public void downloadCommand() {
// command implementation
}
// v4
@Command(name = "download", availabilityProvider = "loginAvailabilityProvider")
public void downloadCommand() {
// command implementation
}The same applies to other customizations such as exception mapping to exit codes, command completion, etc.
In Spring Shell 4, the APIs for programmatic command definition and registration have been revamped to align with the new declarative command definition approach.
The main entry point for programmatic command registration is the CommandRegistry interface. You can create commands using the Command.Builder class and register them with the CommandRegistry. For example:
@Bean
public Command myCommand() {
return Command.builder().name("mycommand").execute(context -> {
context.outputWriter().println("This is my command!");
});
}With Spring Shell 4, programmatic command registration is simplified and more consistent with the declarative approach and any bean of type Command will be automatically registered as a command.
Please refer to the official documentation for more details and examples.
In Spring Shell 4, it is not possible to use several ShellRunner implementations in the same application anymore. This was confusing both in terms of configuration and usage. You can obviously still define multiple shell implementations in the same context if you want, however, you will need to choose which one to use at startup time.
Spring Shell 4 provides 3 implementations of the ShellRunner interface out of the box:
-
SystemShellRunner: A interactive shell implementation based on the standard Java Console (requires no additional dependency). This is a new addition in Spring Shell v4. -
JLineShellRunner: A interactive shell implementation based on JLine (requiresspring-shell-jlinedependency). -
NonInteractiveShellRunner: A non-interactive shell implementation that is designed for scripting and automation scenarios.
By default, Spring Shell will use the SystemShellRunner implementation when using the @EnableCommand annotation:
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.shell.core.ShellRunner;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.shell.core.command.annotation.EnableCommand;
@EnableCommand(SpringShellApplication.class)
public class SpringShellApplication {
public static void main(String[] args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringShellApplication.class);
ShellRunner runner = context.getBean(ShellRunner.class);
runner.run(args);
}
@Command
public void hi() {
System.out.println("Hello world!");
}
}The SystemShellRunner provides basic shell functionalities using the standard Java Console. However, it does not support advanced features such as command history, tab completion, and rich text formatting. If you want to use the JLine-based shell which is more feature-rich, you need to explicitly define a JLineShellRunner bean in your configuration or use the Spring Boot starter that does this for you:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.shell.core.command.annotation.Command;
@SpringBootApplication
public class SpringShellApplication {
public static void main(String[] args) {
// This will start the JLine-based interactive shell
SpringApplication.run(SpringShellApplication.class, args);
}
@Command
public void hi() {
System.out.println("Hello world!");
}
}// TBD