Multiple Approaches to Execute Code After Spring Boot Startup

Nov 15, 2025 · Programming · 12 views · 7.8

Keywords: Spring Boot | Post Startup Execution | ApplicationReadyEvent | CommandLineRunner | ApplicationRunner

Abstract: This article provides an in-depth exploration of various methods to execute custom code after Spring Boot application startup, with focus on ApplicationReadyEvent listeners, CommandLineRunner interface, ApplicationRunner interface, and @PostConstruct annotation. Through detailed code examples and timing analysis, it explains the applicable scenarios, execution order, and best practices for different approaches, helping developers choose the most suitable post-startup execution strategy based on specific requirements.

Spring Boot Startup Lifecycle Overview

During the Spring Boot application startup process, the container goes through several critical phases, each triggering corresponding events. Understanding the timing of these events is crucial for executing post-startup code at the right moment. Spring Boot provides a rich event mechanism that allows developers to inject custom logic at different stages of the application lifecycle.

ApplicationReadyEvent Listener

ApplicationReadyEvent is one of the last events triggered during Spring Boot application startup, indicating that the application is fully started and ready to handle requests. This event fires after all bean initialization is complete and the context refresh is finished, making it an ideal timing for executing post-startup code.

@Component
public class ApplicationStartupListener implements ApplicationListener<ApplicationReadyEvent> {
    
    @Autowired
    private MonitoringService monitoringService;
    
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        // Start directory monitoring after application is fully started
        monitoringService.startDirectoryMonitoring();
    }
}

Using the @EventListener annotation provides a more concise implementation:

@Component
public class StartupService {
    
    @EventListener(ApplicationReadyEvent.class)
    public void initializeAfterStartup() {
        // Execute post-startup initialization logic
        System.out.println("Application is ready to service requests");
    }
}

Direct Execution in Main Method

Another direct approach is to execute initialization code in the Spring Boot application's main method by obtaining beans through ConfigurableApplicationContext. This method offers maximum control flexibility but requires manual context management.

@SpringBootApplication
public class Application {
    
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        
        // Obtain beans and execute post-startup logic
        DirectoryMonitor monitor = context.getBean(DirectoryMonitor.class);
        monitor.startWatching();
        
        // Execute data initialization
        DataInitializer initializer = context.getBean(DataInitializer.class);
        initializer.fillWithTestData();
    }
}

CommandLineRunner Interface Implementation

The CommandLineRunner interface provides another way to execute code after application startup. Spring Boot automatically calls the run method of all beans implementing this interface.

@Component
@Order(1)
public class DirectoryMonitorRunner implements CommandLineRunner {
    
    @Autowired
    private FileWatcherService fileWatcher;
    
    @Override
    public void run(String... args) throws Exception {
        // Start directory monitoring
        fileWatcher.startMonitoring("/path/to/watch");
        
        // Command line arguments accessible
        if (args.length > 0) {
            System.out.println("Command line arguments: " + Arrays.toString(args));
        }
    }
}

ApplicationRunner Interface

ApplicationRunner is similar to CommandLineRunner but provides better encapsulation of command line arguments. It receives an ApplicationArguments object, making it easier to handle arguments with options.

@Component
public class ConfigLoaderRunner implements ApplicationRunner {
    
    @Autowired
    private ConfigurationService configService;
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        // Handle arguments with options
        if (args.containsOption("config")) {
            List<String> configFiles = args.getOptionValues("config");
            configService.loadConfigurations(configFiles);
        }
        
        // Start configuration monitoring
        configService.startConfigurationMonitoring();
    }
}

Bean Initialization Completion Extension Points

Besides application-level post-startup execution, Spring provides bean-level initialization extension points that can execute code immediately after individual bean initialization.

@PostConstruct Annotation

@Component
public class CacheInitializer {
    
    @Autowired
    private CacheManager cacheManager;
    
    @PostConstruct
    public void initializeCache() {
        // Execute immediately after bean initialization
        cacheManager.preloadCommonData();
        System.out.println("Cache initialized successfully");
    }
}

InitializingBean Interface

@Component
public class MessageListenerInitializer implements InitializingBean {
    
    @Autowired
    private MessageListener messageListener;
    
    @Override
    public void afterPropertiesSet() throws Exception {
        // Execute after property setting completion
        messageListener.registerListeners();
    }
}

Execution Order and Timing Analysis

Different extension points have clear sequential relationships in execution timing:

  1. Bean constructor execution
  2. @PostConstruct method or InitializingBean.afterPropertiesSet() method
  3. ApplicationReadyEvent trigger
  4. CommandLineRunner.run() or ApplicationRunner.run() method

This sequence ensures that post-startup logic executes after dependency injection completion, avoiding issues like null pointer exceptions.

Best Practice Recommendations

Based on different usage scenarios, the following best practices are recommended:

Error Handling and Fault Tolerance

When executing code after startup, exception handling must be considered:

@EventListener(ApplicationReadyEvent.class)
public void safeStartupExecution() {
    try {
        // Execute startup logic
        startupService.initialize();
    } catch (Exception e) {
        // Log errors without preventing application startup
        logger.error("Startup initialization failed", e);
    }
}

Through proper error handling, you can ensure that initialization failures of individual components do not affect the normal startup of the entire application.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.