Skip to content

How to resolve springboot CommandLineRunner run not called problem?

1. The purpose of this post

When using CommandLineRunner in Spring Boot applications, we expect its run method to be called when the application starts. However, sometimes the run method is not called, even if the code and settings appear correct.

This article will help you debug and resolve this issue.

2. Environments

  • Spring Boot 1.x or 2.x
  • Java 1.8+

3. The problem code

3.1 pom.xml

The following is the pom.xml content:

pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-issue1</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

3.2 Runner1.java and Runner2.java

There are two CommandLineRunner implementations in this project:

Runner1.java
@Component
public class Runner1 implements CommandLineRunner {
private static Logger logger = LoggerFactory.getLogger(Runner1.class);
@Override
public void run(String... strings) throws Exception {
logger.debug("runner1 started");
}
}
Runner2.java
@Component
public class Runner2 implements CommandLineRunner {
private static Logger logger = LoggerFactory.getLogger(Runner2.class);
@Override
public void run(String... strings) throws Exception {
logger.debug("runner2 started");
}
}

Initially, everything works fine:

2019-03-08 11:21:17.395 DEBUG 91632 --- [ main] com.bswen.sbissue1.runners.Runner1 : runner1 started
2019-03-08 11:21:17.395 DEBUG 91632 --- [ main] com.bswen.sbissue1.runners.Runner2 : runner2 started

However, after adding Runner3.java, Runner1 and Runner2 stop working.

3.3 Adding Runner3.java to cause the problem

Runner3.java
@Component
public class Runner3 implements Runnable {
private static Logger logger = LoggerFactory.getLogger(Runner3.class);
Object lock = new Object();
@Override
@PostConstruct
public void run() {
logger.debug("runner3 started");
try {
synchronized (lock) {
lock.wait();
}
logger.debug("runner3 unlocked");
}catch (Exception ex) {
logger.error("",ex);
}
}
}

In Runner3.java, we set up a lock to block the current thread. The result is:

2019-03-08 11:23:48.206 DEBUG 91645 --- [ main] com.bswen.sbissue1.runners.Runner3 : runner3 started

The logs for Runner1 and Runner2 are not printed. Why?

3.4 The reason

The issue is caused by the @PostConstruct annotation. Runner3 is a Runnable, but it is not executed as a thread. Instead, it runs as a normal method, blocking the Spring Boot starter thread. As a result, Runner1 and Runner2 cannot execute.

3.5 How to resolve it

To fix the issue, remove the @PostConstruct annotation and start Runner3 in a separate thread. Here’s the corrected code:

Runner3.java
@Component
public class Runner3 implements Runnable {
private static Logger logger = LoggerFactory.getLogger(Runner3.class);
Object lock = new Object();
@Override
public void run() {
logger.debug("runner3 started");
try {
synchronized (lock) {
lock.wait();
}
logger.debug("runner3 unlocked");
}catch (Exception ex) {
logger.error("",ex);
}
}
}

Start Runner3 in Runner2:

Runner2.java
@Component
public class Runner2 implements CommandLineRunner {
private static Logger logger = LoggerFactory.getLogger(Runner2.class);
@Autowired
private Runner3 runner3;
@Override
public void run(String... strings) throws Exception {
logger.debug("runner2 started");
(new Thread(runner3)).start();
}
}

Now, the logs show all runners working correctly:

2019-03-08 11:27:59.796 DEBUG 91655 --- [ main] com.bswen.sbissue1.runners.Runner1 : runner1 started
2019-03-08 11:27:59.796 DEBUG 91655 --- [ main] com.bswen.sbissue1.runners.Runner2 : runner2 started
2019-03-08 11:27:59.797 DEBUG 91655 --- [ Thread-3] com.bswen.sbissue1.runners.Runner3 : runner3 started

4. Summary

In this post, we explored why the CommandLineRunner’s run method might not be called in a Spring Boot application. The issue was caused by a blocking operation in a @PostConstruct method, which prevented other runners from executing. By moving the blocking operation to a separate thread, we resolved the issue and ensured all runners worked as expected.

The example source code is available on GitHub.

Final Words + More Resources

My intention with this article was to help others who might be considering solving such a problem. So I hope that’s been the case here. If you still have any questions, don’t hesitate to ask me by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!