Run method on Spring Boot startup

While developing a spring boot application, sometimes we need to run a method or a piece of code at startup. This code can be anything ranging from, logging certain information to setting up database, cron jobs etc. We cannot just put this code in constructor, because required variables or servies may not be initialized yet. This could lead to null pointers or some other exceptions.

Why do we need to run code at spring boot startup?

Call method after start up

We need to run method at application startup for many reasons like,

  • Logging important things or message saying application is started
  • Processing database or files, indexing, creating caches etc.
  • Starting background process like sending notification, fetching data form some queue, etc.

Different ways to run method after startup in spring boot

Each way has its own benefits. Let's look in detail to decide which we should use,

  1. Using CommandLineRunner interface
  2. With ApplicationRunner interface
  3. Spring boot Application events
  4. @Postconstruct annotation on a method
  5. The InitializingBean Interface
  6. Init attribute of @bean annotation

1. Using CommandLineRunner interface

CommandLineRunner is a spring boot functional interface which is used to run code at application startup. It is present under package org.springframework.boot.

In startup process after the context is initialized, spring boot calls its run() method with command-line arguments provided to the application. 

To inform spring boot about our commandlineRunner interface, we can either implement it and add @Component annotation above the class or create its bean using @bean.

Example of implementing CommandLineRunner interface

@Component
public class CommandLineRunnerImpl implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("In CommandLineRunnerImpl ");

        for (String arg : args) {
            System.out.println(arg);
        }
    }
}

Example of creating bean of CommandLineRunner interface

@SpringBootApplication
public class Application {

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

    @Bean
    public CommandLineRunner CommandLineRunnerBean() {
        return (args) -> {
            System.out.println("In CommandLineRunnerImpl ");

            for (String arg : args) {
                System.out.println(arg);
            }
        };
    }
}

We can run application using command line or IDE. Let's take an example when we run the application using arguments as "--status=running"

mvn spring-boot:run -Dspring-boot.run.arguments="--status=running"

OR

mvn package 
java -jar target/<FILENAME.JAR HERE> --status=running

This will produce the following log output:

In CommandLineRunnerImpl
status=running

As we can see, the parameter is not parsed but instead interpreted as a single value "status=running".

To access command line arguments in parsed format we need to use ApplicationRunner interface. We shall look at it shortly.

Spring Boot adds CommandLineRunner interface into the startup process. Hence throwing exception in commandlinerRunner will force Spring boot to abort startup.

We can create multiple CommandLineRunners in one application. Using the Ordered interface or @Order annotation we can configure the order in which they should run. Lower value means higher the priority. By default all the components are created with lowest priority. That is why components without order configuration will be called last.

We can use order annotation as shown below

@Component
@Order(1)
public class CommandLineRunnerImpl implements CommandLineRunner {
    ........
}

2. With ApplicationRunner Interface

As discussed earlier, to access parsed arguments we need to use ApplicationRunner interface. ApplicationRunner interface provides run method with ApplicationArguments instead of raw string array.

ApplicationArguments is an interface which is available from srping boot 1.3 under the package org.springframework.boot.

It provides different ways to access the arguments as below

String[] GetSourceArgs()Gives unprocessed arguments that were passed to the application
Set<String> getOptionNames()Names of all optional arguments, optional arguments are preceded by "--" eg: –name= "stacktrace"
List<String> getNonOptionArgs()Returns unprocessed non-optional arguments. Arguments without "--"
boolean containsOption(String name) Checks if name is present in the optional arguments or not
List<String> getOptionValues(String name)Gives the argument value by name

Method getOptionValues returns value list because, the argument value can be array as we can use same key more than once in the command-line.
For example –name= "stacktrace" -- Port=8080 --name="guru"

Example of Application runner as interface implementation

Let's run the program below using "status=running --mood=happy 10 --20" arguments and let's understand the output

@Component
public class ApplicationRunnerImpl implements ApplicationRunner {

   @Override
   public void run(ApplicationArguments args) throws Exception {

      System.out.println("ApplicationRunnerImpl Called");

//print all arguemnts: arg: status=running, arg: --mood=happy, 10, --20
      for (String arg : args.getSourceArgs()) {
         System.out.println("arg: "+arg);
      }
      System.out.println("NonOptionArgs: "+args.getNonOptionArgs()); //[status=running,10]
      System.out.println("OptionNames: "+args.getOptionNames());  //[mood, 20]

     System.out.println("Printing key and value in loop:");
      for (String key : args.getOptionNames()) {
         System.out.println("key: "+key);     //key: mood  //key: 20
         System.out.println("val: "+args.getOptionValues(key)); //val:[happy] //val:[]
      }
   }
}

Output:

ApplicationRunnerImpl Called
arg: status=running
arg: --mood=happ
arg: 10
arg: --20
NonOptionArgs: [status=running , 10]
OptionNames: [mood, 20]
Printing key and value in loop:
key: mood
val: [happy]
key: 20
val: []

CommandLineRunner and ApplicationRunner have similar features like

  • An exception in the run() method will abort application startup 
  • Several ApplicationRunners can be ordered using Ordered interface or @Order annotation

Most important point to note that the Order is shared between CommandLineRunners and ApplicationRunners. That means the execution order could be mixed between commandlinerRunner and applicationRunner.

3. Application event in Spring Boot

Spring framework triggers different events in different situations. It also triggers many events in startup process. We can use these events to execute our code, for example ApplicationReadyEvent can be used to execute code after spring boot application starts up.

If we don't need command-line arguments, this is the best way to execute code after application starts up.

@Component
public class RunAfterStartup{

@EventListener(ApplicationReadyEvent.class)
public void runAfterStartup() {
    System.out.println("Yaaah, I am running........");
}

Output:

Yaaah, I am running........

Some most important events in spring boot are,

  • ApplicationContextInitializedEvent : triggered after ApplicationContext is prepared and ApplicationContextInitializers are called but before bean definitions are loaded
  • ApplicationPreparedEvent : triggered after bean definitions are loaded
  • ApplicationStartedEvent : triggered after context has been refreshed but before command-line and application runners are called
  • ApplicationReadyEvent : triggered after any application and command-line runners are called
  • ApplicationFailedEvent : triggered if there is an exception on startup

Multiple ApplicationListeners can be created. They can be ordered with the @Order annotation or Ordered interface.

The order is shared with other same type of ApplicationListeners but not with ApplicationRunners or CommandLineRunners.

4. @Postconstruct annotation on a method

A method can be marked with @PostConstruct annotation. Whenever a method is marked with this annotation, it will be called immediately after the dependency injection.

A @PostConstruct method is linked to specific class hence it should be used for class specific code only. There can only be one method per class with postConstruct annotation.

@Component
public class PostContructImpl {

	public PostContructImpl() {
		System.out.println("PostContructImpl Constructor called");
	}

	@PostConstruct
	public void runAfterObjectCreated() {
		System.out.println("PostContruct method called");
	}

}

Output:

PostContructImpl Constructor called
postContruct method called

Point to note is that if class is marked as lazy, that means the class is created when requested. After that the method marked with @postConstruct annotation will be executed.

The method marked with postConstruct annotation can have any name, however must not have any parameters. It must be void and should not be static.

Please note that @postConstruct annotation is part of Java EE module and it is marked as deprecated in Java 9 and removed in Java 11. We can still use it by adding java.se.ee into the application.

5. The InitializingBean Interface

The InitializingBean solution works exactly the similar to the postConstruct annotation. Instead of using annotation we have to implement an InitializingBean interface. Then we need to override void afterPropertiesSet() method.

InitializingBean is a part of org.springframework.beans.factory package.

@Component
public class InitializingBeanImpl implements InitializingBean {
    public InitializingBeanImpl() {
        System.out.println("InitializingBeanImpl Constructor called");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBeanImpl afterPropertiesSet method called");
    }
}

You must be thinking what happens if we use both @PostConstruct annotation and InitializingBean together. Well in that case @PostConstruct method will be called before the InitializingBean's afterPropertiesSet() method.

6. Init attribute of @bean annotation

We can provide a method using initMethod property in the @Bean annotation. This method will becalled after bean is initialized.

The method provided in initMethod must be void and should not have any arguments. This method can even be private.

public class BeanInitMethodImpl {
 
    public void runAfterObjectCreated() {
        System.out.println("yooooooooo......... someone called me");
    }
} 

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

    @Bean(initMethod="runAfterObjectCreated")
    public BeanInitMethodImpl getFunnyBean() {
        return new BeanInitMethodImpl();
    }
 }

Output:

yooooooooo......... someone called me

If you have InitializingBean implementation and initMethod property of @Bean annotation for the same class, then afterPropertiesSet method of InitializingBean will be called befor ehte initMethod.

Combining different approaches:

Lastly, sometimes we may need to combine multiple options. Then they will execute in the following order,

  • Constructor
  • PostContruct method
  • afterPropertiesSet method
  • Bean init Method
  • ApplicationStartedEvent
  • ApplicationRunner Or CommandLineRunner depends on Order
  • ApplicationReadyEvent

Fast track reading

  • There are different ways to run code after spring boot application startup
  • We can use CommandLineRunner or ApplicationRunner Interface
  • Use ApplicationRunner interface to access parsed arguments  instead of raw string array
  • Spring boot event executes code on application startup
  • Method marked with @PostConstruct annotation executes after the object initialization
  • afterPropertiesSet() method of InitializingBean Interfacecalled after the object initialization
  • @Bean annotation has an attribute 'initMethod' to provide method which will be called after bean initialization

Related topics

Leave a Reply

Your email address will not be published. Required fields are marked *