SoFunction
Updated on 2025-04-15

SpringBoot CommandLineRunner application execution example after starting code

introduction

In enterprise-level application development, we often need to perform some initialization operations after the application is started, such as preloading cached data, creating default administrator accounts, data migration and other tasks.

Spring Boot provides the CommandLineRunner interface, allowing developers to achieve these needs gracefully.

1. CommandLineRunner Basics

CommandLineRunner is an interface provided by Spring Boot, which is used to execute specific code logic after the Spring application context is fully initialized and before the application provides services. This interface only contains one run method, and the method parameters are command line parameters passed in when the application starts.

The CommandLineRunner interface is defined as follows:

@FunctionalInterface
public interface CommandLineRunner {
    /**
      * Callback after SpringApplication starts
      * @param args Command line parameters from the application
      * @throws Exception If an error occurs
      */
    void run(String... args) throws Exception;
}

When a Spring Boot application is started, the Spring container will automatically retrieve and execute all beans that implement the CommandLineRunner interface after the initialization of all beans is completed.

This mechanism provides an explicit initialization opportunity for the application to ensure that all dependencies are ready.

2. Basic usage

2.1 Create a CommandLineRunner implementation

The easiest way to implement the CommandLineRunner interface is to create a component class and implement it:

package ;

import org.;
import org.;
import ;
import ;

/**
  * Simple CommandLineRunner implementation
  * Used to demonstrate basic usage
  */
@Component
public class SimpleCommandLineRunner implements CommandLineRunner {

    private static final Logger logger = ();

    @Override
    public void run(String... args) throws Exception {
        ("The application starts and starts the initialization operation...");
        
        // Execute initialization logic        ("Initialization operation completed");
        
        // If necessary, you can access the command line parameters        if ( > 0) {
            ("Received command line arguments:");
            for (int i = 0; i < ; i++) {
                ("parameter {}: {}", i, args[i]);
            }
        }
    }
}

With the @Component annotation, Spring will automatically scan and register the bean, and then call its run method after the application starts.

2.2 Using Lambda Expressions

Since CommandLineRunner is a functional interface, we can also use Lambda expressions to simplify the code. This method is suitable for implementing simple initialization logic:

package ;

import org.;
import org.;
import ;
import ;
import ;

/**
  * Create CommandLineRunner via @Bean method
  */
@Configuration
public class AppConfig {

    private static final Logger logger = ();

    @Bean
    public CommandLineRunner initDatabase() {
        return args -> {
            ("Initialize the database connection pool...");
            // Execute database initialization logic        };
    }
    
    @Bean
    public CommandLineRunner loadCache() {
        return args -> {
            ("Preload cached data...");
            // Execute cache warm-up logic        };
    }
}

In this example, we create two CommandLineRunner instances through the @Bean method, which are responsible for database initialization and cache warm-up.

3. Advanced features

3.1 Execution sequence control

When multiple CommandLineRunners exist in the application, it may be necessary to control their execution order.

Spring provides @Order annotation (or implementing the Ordered interface) to achieve this requirement:

package ;

import org.;
import org.;
import ;
import ;
import ;

/**
  * CommandLineRunner with execution order
  * The smaller the Order value, the higher the priority, and the earlier the execution
  */
@Component
@Order(1) // High priority, execute firstpublic class DatabaseInitializer implements CommandLineRunner {

    private static final Logger logger = ();

    @Override
    public void run(String... args) throws Exception {
        ("Step 1: Initialize the database...");
        // Database initialization logic    }
}

@Component
@Order(2) // Sub-prioritypublic class CacheWarmer implements CommandLineRunner {

    private static final Logger logger = ();

    @Override
    public void run(String... args) throws Exception {
        ("Step 2: Preheat the cache...");
        // Cache warm-up logic    }
}

@Component
@Order(3) // Last executionpublic class NotificationInitializer implements CommandLineRunner {

    private static final Logger logger = ();

    @Override
    public void run(String... args) throws Exception {
        ("Step 3: Initialize the notification service...");
        // Notification service initialization logic    }
}

Through the @Order annotation, we can accurately control the execution order of each initialization task to ensure that the dependencies are satisfied. It is worth noting that the smaller the Order value, the higher the priority, and the earlier the execution.

3.2 Dependency injection

CommandLineRunner, as a Spring-managed Bean, can make full use of the dependency injection mechanism:

package ;

import ;
import ;
import org.;
import org.;
import ;
import ;
import ;

/**
  * Demonstrate using dependency injection in CommandLineRunner
  */
@Component
public class SystemInitializer implements CommandLineRunner {

    private static final Logger logger = ();
    
    private final UserService userService;
    private final SystemConfigService configService;
    
    @Autowired
    public SystemInitializer(UserService userService, SystemConfigService configService) {
         = userService;
         = configService;
    }

    @Override
    public void run(String... args) throws Exception {
        ("System initialization begins...");
        
        // Create a default administrator account        if (!()) {
            ("Create a default administrator account");
            ();
        }
        
        // Loading system configuration        ("Loading system configuration into memory");
        ();
        
        ("System initialization completed");
    }
}

This example shows how to inject and use service components in CommandLineRunner to implement more complex initialization logic.

3.3 Exception handling

The run method of CommandLineRunner allows exceptions to be thrown. If an exception is thrown during execution, the Spring Boot application will not start properly.

This feature can be used to ensure that critical initialization operations must be completed successfully:

package ;

import org.;
import org.;
import ;
import ;

/**
  * Demonstrate exception handling in CommandLineRunner
  */
@Component
public class CriticalInitializer implements CommandLineRunner {

    private static final Logger logger = ();

    @Override
    public void run(String... args) throws Exception {
        ("Perform a critical initialization operation...");
        
        try {
            // Execute possible failures            boolean success = performCriticalOperation();
            
            if (!success) {
                // If the operation fails to complete successfully, prevent the application from starting                throw new RuntimeException("Critical initialization operation failed, application cannot be started");
            }
            
            ("Key initialization operation is completed");
        } catch (Exception e) {
            ("An error occurred during initialization: {}", ());
            // Rethrow an exception to prevent the application from starting            throw e;
        }
    }
    
    private boolean performCriticalOperation() {
        // Simulate the execution of key operations        return true; // Return operation result    }
}

In this example, if the critical initialization operation fails, the application will not be started, thus preventing the system from running in an incomplete state.

4. Practical application scenarios

CommandLineRunner is suitable for a variety of practical scenarios. Here are some common applications:

4.1 Data migration

During system upgrades or data structure changes, you can use CommandLineRunner to perform data migration operations:

@Component
@Order(1)
public class DataMigrationRunner implements CommandLineRunner {

    private final DataMigrationService migrationService;
    private static final Logger logger = ();
    
    @Autowired
    public DataMigrationRunner(DataMigrationService migrationService) {
         = migrationService;
    }

    @Override
    public void run(String... args) throws Exception {
        ("Check the database version and perform the migration...");
        
        // Get the current database version        String currentVersion = ();
        ("Current database version: {}", currentVersion);
        
        // Perform migration        boolean migrationNeeded = ();
        if (migrationNeeded) {
            ("Data migration is required");
            ();
            ("Data migration is completed");
        } else {
            ("No data migration required");
        }
    }
}

4.2 Task Scheduling Initialization

For applications that use dynamic task scheduling, the schedule configuration can be loaded from the database at startup:

@Component
public class SchedulerInitializer implements CommandLineRunner {

    private final TaskSchedulerService schedulerService;
    private final TaskDefinitionRepository taskRepository;
    private static final Logger logger = ();
    
    @Autowired
    public SchedulerInitializer(TaskSchedulerService schedulerService, 
                               TaskDefinitionRepository taskRepository) {
         = schedulerService;
         = taskRepository;
    }

    @Override
    public void run(String... args) throws Exception {
        ("Initialize the task scheduler...");
        
        // Load the task definition from the database        List<TaskDefinition> tasks = ();
        ("Loaded{}Scheduling tasks", ());
        
        // Register the task to the scheduler        for (TaskDefinition task : tasks) {
            (task);
            ("Register a task: {}, cron: {}", (), ());
        }
        
        ("Task Scheduler Initialization Completed");
    }
}

4.3 External system connection test

At the start of the application, you can test the connection status with the critical external system:

@Component
public class ExternalSystemConnectionTester implements CommandLineRunner {

    private final List<ExternalSystemConnector> connectors;
    private static final Logger logger = ();
    
    @Autowired
    public ExternalSystemConnectionTester(List<ExternalSystemConnector> connectors) {
         = connectors;
    }

    @Override
    public void run(String... args) throws Exception {
        ("Test external system connection...");
        
        for (ExternalSystemConnector connector : connectors) {
            String systemName = ();
            ("Test Connect to: {}", systemName);
            
            try {
                boolean connected = ();
                if (connected) {
                    ("{} Connection successfully", systemName);
                } else {
                    ("{} Connection failed,But it does not prevent the application from starting", systemName);
                }
            } catch (Exception e) {
                ("{} Connection exception: {}", systemName, ());
                // Decide whether to throw an exception to prevent the application from starting based on the system importance            }
        }
        
        ("External system connection test completed");
    }
}

V. Best Practices

When using CommandLineRunner, the following best practices can help developers take advantage of this feature more effectively:

  • Single responsibilities: Each CommandLineRunner should focus on a specific initialization task and follow the single responsibility principle.
  • Reasonable grouping: Related initialization tasks can be organized in the same CommandLineRunner to reduce code fragmentation.
  • Asynchronous execution: For time-consuming initialization tasks, consider using asynchronous execution to avoid extending application startup time:
@Component
public class AsyncInitializer implements CommandLineRunner {

    private final TaskExecutor taskExecutor;
    private static final Logger logger = ();
    
    @Autowired
    public AsyncInitializer(TaskExecutor taskExecutor) {
         = taskExecutor;
    }

    @Override
    public void run(String... args) throws Exception {
        ("Start the asynchronous initialization task...");
        
        (() -> {
            try {
                ("Async task starts execution");
                (5000); // Simulation time-consuming operation                ("Async task execution is completed");
            } catch (InterruptedException e) {
                ().interrupt();
                ("Async task is interrupted", e);
            } catch (Exception e) {
                ("Async task execution failed", e);
            }
        });
        
        ("Asynchronous initialization task has been submitted, the application continues to start");
    }
}
  • Elegant downgrade: For non-critical initialization tasks, achieve elegant downgrades to avoid preventing the entire application from starting due to secondary functional failures.
  • Reasonable log: Use appropriate log levels to record the initialization process to facilitate problem troubleshooting and performance analysis.
  • Conditional execution: Decide whether to perform specific initialization tasks based on the environment or configuration conditions, and enhance flexibility:
@Component
@ConditionalOnProperty(name = "", havingValue = "true")
public class ConditionalInitializer implements CommandLineRunner {

    private static final Logger logger = ();

    @Override
    public void run(String... args) throws Exception {
        ("Execute conditional initialization tasks, only when configuration is enabled");
        // Initialization logic executed only under certain conditions    }
}

Summarize

Spring Boot's CommandLineRunner interface provides an elegant mechanism for applications to execute initialization code after startup is completed.

By implementing this interface, developers can ensure that the necessary preparations have been completed before the application provides services to the outside world.

Combined with the @Order annotation, the execution order of multiple initialization tasks can be accurately controlled, and the dependency injection mechanism makes various service components easy to use during the initialization process.

In practical applications, CommandLineRunner can be used in various scenarios such as data migration, cache warm-up, and connection testing.

By following the single responsibility principle, organizing code reasonably, implementing asynchronous execution and elegant downgrades, you can build a more robust and efficient Spring Boot application.

The above is personal experience. I hope you can give you a reference and I hope you can support me more.