In Spring Boot, the CommandLineRunner and ApplicationRunner are two utility interfaces that we can use to execute code when application is started. However, all beans that implement these interfaces will be invoked by Spring Boot, and it takes some effort to execute only a portion of them. This is especially important when you are developing a console application with multiple entry points. In this article, we will use several techniques to achieve this goal.
Put CommandLineRunner in different packages
By default, @SpringBootApplication will scan components (or beans) in current and descendant packages. When multiple CommandLineRunners are discovered, Spring will execute them all. So the first approach will be separating those runners into different packages.
1 | package com.shzhangji.package_a; |
If there is a JobB in package_b, these two jobs will not affect each other. But one problem is, when executing JobA, only components defined under package_a will be scanned. So if JobA wants to use a service in com.shzhangji.common package, we have to import this class explicitly:
1 | package com.shzhangji.package_a; |
If there are multiple classes or packages that you want to import, you may as well change the base packages property:
1 |
|
Conditional scanning of components
So the basic idea is to expose only one CommandLineRunner to Spring’s component scanning mechanism. Luckily Spring Framework provides the @Conditional annotation that can be used to filter beans based on system property, profile, or more complex conditions. As a matter of fact, Spring Boot’s auto configuration feature is largely based on @Conditional. For instance:
1 |
|
When initializing the embedded web server, Spring will check if Tomcat or Jetty is on the classpath (@ConditionalOnClass), and create the corresponding beans. The configuration class itself is also conditionally processed in a web environment (@ConditionalOnWebApplication).
In our situation, we shall use the @ConditionalOnProperty annotation, that filters beans based on system properties. Say we accept a property named job, and only create the CommandLineRunner bean when their values match.
1 |
|
To run this example in IDEA, use the following configuration:

The -Djob in VM options and --job in program arguments are equivalent, so you only need to specify once. This property can also be set in a configuration file.
Similar to @Conditional, Spring Profiles can also be used to filter beans.
1 |
|
You can either activate the profile in command line arguments or environment variables.

Write a JobDispatcher
Lastly, we can always add a middle layer to solve the problem, i.e. a JobDispatcher that decides which Runnable to run. Only this time, we use the ApplicationRunner instead, because it will help us parsing the command line arguments.
1 |
|
When we pass --job=JobDispatcherA on the command line, the dispatcher will try to locate the job class, and initialize it with beans defined in context.
1 |
|
Example code can be found on GitHub.