Can We Run Initialization Code?

Sometimes you want to run specific code immediately after Spring Boot starts. For example, initialize reference values or notify monitoring systems that startup completed. Spring Boot provides several ways to execute code at startup.


CommandLineRunner

First is CommandLineRunner. The example below declares an anonymous implementation via @Bean. Code runs after application startup.

@SpringBootApplication
public class JavaExampleApplication {

	public static void main(String[] args) {
		SpringApplication.run(JavaExampleApplication.class, args);
	}
	
	@Bean
	public CommandLineRunner test(MyRepository myRepository) {
		return args -> {
			myRepository.sayHello();
            // etc...
		};
	}
}

Expanded form:

@Bean
public CommandLineRunner test(MyRepository myRepository) {
    return new CommandLineRunner() {
        @Override
        public void run(String... args) throws Exception {
            myRepository.sayHello();
            // etc...
        }
    };
}

In many projects, this logic is separated into dedicated classes instead of main class. In that case, use @Component on a separate class instead of @Bean.

When a class implementing CommandLineRunner is registered as a bean, its overridden run method executes after startup completes.

@Component
public class MyCommandLineRunner implements CommandLineRunner {
	@Override
	public void run(String... args) throws Exception {
		System.out.println("CommandLineRunner!");
	}
}

ApplicationRunner

Next is ApplicationRunner. Difference from CommandLineRunner is parameter type. It receives ApplicationArguments instead of String. Also, CommandLineRunner was introduced in 1.0.0, while ApplicationRunner was added in 1.3.0. Both are old, but ApplicationRunner is relatively newer.

@Component
public class MyApplicationRunner implements ApplicationRunner {
	@Override
	public void run(ApplicationArguments args) throws Exception {
		System.out.println("ApplicationRunner!");
	}
}


ApplicationReadyEvent

Finally, use @EventListener. Spring publishes ApplicationReadyEvent when the app is ready to serve requests. So with @EventListener, you can run code when startup fully completes.

@SpringBootApplication
public class JavaExampleApplication {

	public static void main(String[] args) {
		SpringApplication.run(JavaExampleApplication.class, args);
	}

	@EventListener(ApplicationReadyEvent.class)
	public void init() {
		System.out.println("EventListener!");

	}
}


Can We Use Them Together? In What Order?

Yes, you can use all of them together. You can control order using @Order.

@Order(1)
@Component
public class MyCommandLineRunner implements CommandLineRunner {
	@Override
	public void run(String... args) throws Exception {
		System.out.println("CommandLineRunner!");
	}
}

@Order(2)
@Component
public class MyApplicationRunner implements ApplicationRunner {
	@Override
	public void run(ApplicationArguments args) throws Exception {
		System.out.println("ApplicationRunner!");
	}
}

Without explicit order, ApplicationRunner executes before CommandLineRunner.

You can verify this by reading internals of SpringApplication. In callRunners, insertion order differs by runner type. If @Order is present, priority is adjusted.

private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners); // sorted if `@Order` is declared.
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

ApplicationReadyEvent runs later than runner-based callbacks. You can confirm this by debugging publishEvent in ApplicationContext. Set breakpoints in callRunners and publishEvent, then run in debug mode.

You will see exactly when each event and callback executes.