In this post, We will take a look at Command line runner in Spring Boot and how to implement them properly with an example.
Typical Java Implementation
Let’s take a small example in pure java.
public class Adder {
public static void main(String[] args) {
int a = Integer.parseInt(args[0]);
int b = Integer.parseInt(args[1]);
System.out.println(a + b);
}
}
Code language: JavaScript (javascript)
The above class is an example of a command line application. The application takes two command line arguments, then calculates their sum and prints it after that the application exits. But the real world command line applications can be quite complex. This is where spring boot’s CommandLineRunner
interface comes into the picture.
Implementing CommandLineRunner
For this example, I created a project from Spring Initializer without any dependencies. But the code will work along with any Spring Boot Starter. If you have done this correctly, the pom.xml should only contain spring-boot-starter
as a dependency (and the boilerplate for test dependencies). When we run this empty project, You will see the spring example application booting up and dying just after few moments. At this point, the project is useless. Let’s make this application print hello world by implementing CommandLineRunner
as shown below.
@SpringBootApplication
public class CommandlineApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(CommandlineApplication.class, args);
}
@Override
public void run(String... args) {
System.out.println("Hello World!");
}
}
Code language: JavaScript (javascript)
The above code prints Hello World at the end of the log output.
Here is how it works. The auto-configuration looks for any CommandLineRunner
component in the classpath and calls the run(String[])
method. This would allow the components that implement CommandLineRunner
to have access to the application context as well as the application parameters. For example, You can print all the beans in the current context as shown below.
@SpringBootApplication
public class CommandlineApplication implements CommandLineRunner {
Logger logger = LoggerFactory.getLogger(CommandlineApplication.class);
public static void main(String[] args) {
SpringApplication.run(CommandlineApplication.class, args);
}
@Autowired
ApplicationContext applicationContext;
@Override
public void run(String... args) {
Map<String, Object> beansOfType = applicationContext.getBeansOfType(Object.class);
beansOfType.forEach((s, o) -> {
logger.info("{} - {}", s, o.getClass());
});
}
}
Code language: JavaScript (javascript)
Just try to run the above code yourself, and you will see the results.
Making use of Command-line Arguments
You may wonder why we can’t achieve the same with a @PostConstruct
or an @EventListener(ApplicationReadyEvent.class)
annotation. Events and PostConstruct methods does not have access to the command-line arguments. This is the reason why CommandLineRunner
exists. This example lets you list all the beans that has a bean name containing a search text from commandline.
@SpringBootApplication
public class CommandlineApplication implements CommandLineRunner {
Logger logger = LoggerFactory.getLogger(CommandlineApplication.class);
public static void main(String[] args) {
SpringApplication.run(CommandlineApplication.class, args);
}
@Autowired
ApplicationContext applicationContext;
@Override
public void run(String... args) {
if (args.length > 0) {
Map<String, Object> beansOfType = applicationContext.getBeansOfType(Object.class);
beansOfType.forEach((s, o) -> {
if (o.getClass().getCanonicalName().contains(args[0])) {
logger.info("{} - {}", s, o.getClass());
}
});
}
}
}
Code language: JavaScript (javascript)
Calling the application with a commandline argument Log
prints out only the following two lines in the log.
springBootLoggingSystem - class org.springframework.boot.logging.logback.LogbackLoggingSystem
springBootLoggerGroups - class org.springframework.boot.logging.LoggerGroups
Code language: CSS (css)
Command Line Runner as components
So it is clear why this command line runner interface exists as part of spring boot. We should use CommandLineRunner
if we want to initiate any process that needs both the autowiring capabilities of the spring context and the application parameters.
One thing to note here is that I made my main class to implement CommandLineRunner
. However, It is not advisable nor necessary. You can define your own component that extends the interface, and it should be fine. For example, the below works too.
@Component
public class HelloWorldCommandLineRunner implements CommandLineRunner {
Logger logger = LoggerFactory.getLogger(CommandlineApplication.class);
@Override
public void run(String... args) throws Exception {
logger.info("Hello world from command line runner");
}
}
Code language: JavaScript (javascript)
Multiple CommandLineRunners
You can also define multiple CommandLineRunner
Implementations. The only catch here is that each of these components will run in series. If one of them is going to run for long, then the other runners will suffer. Also, the default Spring behaviour for beans of same type is that they are sorted by bean name in alphabetical order. So you may see the execution of components in the same order.