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?
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,
- Using CommandLineRunner interface
- With ApplicationRunner interface
- Spring boot Application events
- @Postconstruct annotation on a method
- The InitializingBean Interface
- 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
01 02 03 04 05 06 07 08 09 10 11 12 | @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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | @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”
1 | mvn spring-boot:run -Dspring-boot.run.arguments= "--status=running" |
OR
1 2 | mvn package java -jar target/<FILENAME.JAR HERE> --status=running |
This will produce the following log output:
1 2 | 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
1 2 3 4 5 | @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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 | @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:
01 02 03 04 05 06 07 08 09 10 11 12 | 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.
1 2 3 4 5 6 7 | @Component public class RunAfterStartup{ @EventListener (ApplicationReadyEvent. class ) public void runAfterStartup() { System.out.println( "Yaaah, I am running........" ); } |
Output:
1 | 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.
01 02 03 04 05 06 07 08 09 10 11 12 13 | @Component public class PostContructImpl { public PostContructImpl() { System.out.println( "PostContructImpl Constructor called" ); } @PostConstruct public void runAfterObjectCreated() { System.out.println( "PostContruct method called" ); } } |
Output:
1 2 | 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.
01 02 03 04 05 06 07 08 09 10 | @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.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | 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:
1 | 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
- Spring boot project setup guide
- Springboot introduction tutorial
- Beginner tutorial for Spring data Java
- Method name to nested object query
- Spring boot custom banner generation
Published on Java Code Geeks with permission by Stacktraceguru, partner at our JCG program. See the original article here: Run method on Spring Boot startup Opinions expressed by Java Code Geeks contributors are their own. |