1. Overview

Spring Boot is well-known for its web application development, but it has a lot of other powerful tools.

In this quick tutorial, we’ll create a Spring Boot console-based application with Kotlin using the CommandLineRunner functional interface.

2. Setup Dependencies

For this application, we need just the Spring Boot starter dependency using Gradle:

implementation("org.springframework.boot:spring-boot-starter")

or using Maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

3. CommandLineRunner

The CommandLineRunner is a functional interface with a run() method that receives a raw String array. After loading the application context, Spring Boot will automatically call the run methods of beans implementing this interface:

@FunctionalInterface
public interface CommandLineRunner {
    void run(String... args) throws Exception;
}

Let’s implement the CommandLineRunner interface in our main class:

@SpringBootApplication
class SpringBootConsoleApplication: CommandLineRunner {

    private val log = LoggerFactory.getLogger(SpringBootConsoleApplication::class.java)

    override fun run(vararg args: String?) {
        args.forEach { log.info("Application args: $it") }
    }
}

fun main(args: Array<String>) {
    runApplication<SpringBootConsoleApplication>(*args)
}

Then, if we run the application with the argument “hello” we should see the following output:

INFO 61619 --- [main] c.b.c.SpringBootConsoleApplicationKt     : Starting SpringBootConsoleApplicationKt
INFO 61619 --- [main] c.b.c.SpringBootConsoleApplicationKt     : No active profile set, falling back to 1 default profile: "default"
INFO 61619 --- [main] c.b.c.SpringBootConsoleApplicationKt     : Started SpringBootConsoleApplicationKt in 1.153 seconds (JVM running for 1.967)
INFO 61619 --- [main] c.b.c.SpringBootConsoleApplication       : Application args: hello

Note that run() was called after our Spring Boot application started.

4. User Input Interaction

There are many ways to read user input from the command line in Kotlin. One way is using the readlnOrNull() method:

override fun run(vararg args: String?) {
    log.info("Please type your name:")
    val name = readlnOrNull()
    log.info("Hello $name. This is a Spring Boot Console Application")
}

When we run our application, it’ll wait for user input:

INFO 62997 --- [main] c.b.c.SpringBootConsoleApplicationKt     : Starting SpringBootConsoleApplicationKt using Java 17.0.4 on Fabios-MacBook-Pro.local with PID 62997 (/Users/fabiomiyasato/dev/git/kotlin-tutorials/spring-boot-kotlin-2/target/classes started by fabiomiyasato in /Users/fabiomiyasato/dev/git/kotlin-tutorials/spring-boot-kotlin-2)
INFO 62997 --- [main] c.b.c.SpringBootConsoleApplicationKt     : No active profile set, falling back to 1 default profile: "default"
INFO 62997 --- [main] c.b.c.SpringBootConsoleApplicationKt     : Started SpringBootConsoleApplicationKt in 1.217 seconds (JVM running for 1.687)
INFO 62997 --- [main] c.b.c.SpringBootConsoleApplication       : Please type your name:

We can type anything, press enter, and the application will continue and then finish:

INFO 62997 --- [main] c.b.c.SpringBootConsoleApplication       : Please type your name:
new user
INFO 62997 --- [main] c.b.c.SpringBootConsoleApplication       : Hello new user. This is a Spring Boot Console Application

5. Order

Most console applications will have a single CommandLineRunner implementation. If we need more, we can create components instead of implementing CommandLineRunner in the main class. In this case, we can use Spring’s @O**rder annotation to control the sorting order of components:

@Component
@Order(0)
class CommandLineFirstComponent: CommandLineRunner {

    private val log = LoggerFactory.getLogger(CommandLineFirstComponent::class.java)

    override fun run(vararg args: String?) {
        log.info(CommandLineFirstComponent::class.simpleName)
    }
}

and the second one:

@Component
@Order(1)
class CommandLineSecondComponent: CommandLineRunner {

    private val log = LoggerFactory.getLogger(CommandLineSecondComponent::class.java)

    override fun run(vararg args: String?) {
        log.info(CommandLineSecondComponent::class.simpleName)
    }
}

These components are very similar. Both are annotated with @Component, which indicates that those are beans that should be detected automatically and implement the CommandLineRunner interface logging its own class name.

After running our application, we see the following output:

INFO 62516 --- [main] c.b.c.SpringBootConsoleApplicationKt     : Starting SpringBootConsoleApplicationKt
INFO 62516 --- [main] c.b.c.SpringBootConsoleApplicationKt     : Started SpringBootConsoleApplicationKt in 1.182 seconds (JVM running for 1.668)
INFO 62516 --- [main] c.b.c.CommandLineFirstComponent          : CommandLineFirstComponent
INFO 62516 --- [main] c.b.c.CommandLineSecondComponent         : CommandLineSecondComponent

We can see that CommandLineFirstComponent was executed before CommandLineSecondComponent.

6. Conclusion

Console-based applications may be useful in many situations. In this article, we learned how to use Spring Boot to power up a console application. Using Spring Boot to create a console application might look like an overhead. However, if we base our application on the framework, we can make use of all its benefits, like dependency injection. Spring also provides a specific project called Spring Shell if we need a CLI (command line interface) using annotations in a more “structured” way than the simple console app we created.

The source code of this article is available over on GitHub.