Springboot best practice: Add methods to run when Spring Boot is started
When developing Spring Boot applications, sometimes we need to run methods or pieces of code at startup time. This code can be anything from logging some information to setting up databases, cron jobs, and so on. We can’t just put this code in the constructor because the required variables or services might not be initialized. This can result in null Pointers or some other exception.
Why do we need to run the code when Spring Boot starts?
We need to run methods at application startup for a number of reasons,
- Log something important or a message that the application has started
- Process databases or files, build indexes, create caches, etc.
- Start background processes, such as sending notifications, fetching data from some queue, and so on.
Different methods to run methods after starting in Spring Boot
Each approach has its own advantages. Let’s take a closer look to determine which one to use,
- Use the CommandLineRunner interface
- With the ApplicationRunner interface
- Spring Boot application events
- Comments for the @postconstruct method
- InitializingBean interface
- The Init property of the @bean annotation
1. Use the CommandLineRunner interface
CommandLineRunner is a spring-start feature interface for running code at application startup time. It is located under the package org.springFramework.boot.
During startup after context initialization, Spring Boot invokes its run () method with the command-line arguments provided to the application.
To notify Spring Boot of our commandlineRunner interface, we can implement it and add the @Component annotation above the class, or create its bean using the @Bean.
An example of implementing the 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); }}}Copy the code
An example of creating a CommandLineRunner interface Bean
@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); }}; }}Copy the code
We can use the command line or the IDE to run the application. Let’s take an example when we run the application with the parameter “-status = RUNNING”
mvn spring-boot:run -Dspring-boot.run.arguments="--status=running"
Copy the code
either
mvn package
java -jar target/<FILENAME.JAR HERE> --status=running
Copy the code
This produces the following log output:
In CommandLineRunnerImpl
status=running
Copy the code
As we can see, this parameter is not parsed, but interpreted as a single value of “status = RUNNING”.
To access command line arguments in parsed formats, we need to use the ApplicationRunner interface. We’ll look at that later.
Spring Boot adds the CommandLineRunner interface to the Boot process. Therefore, throwing an exception in Commandliner Runner will force Spring startup to abort startup.
We can create multiple CommandLinerunners in one application. Using the Ordered interface or the @Order annotation, we can configure their running Order. A lower value indicates a higher priority. By default, all components are created with the lowest priority. This is why components with no order configuration will be called last.
We can use an order comment, as shown below
@Component
@Order(1)
public class CommandLineRunnerImpl implements CommandLineRunner {
........
}
Copy the code
2. Have the ApplicationRunner interface
As mentioned earlier, to access parsed parameters, we need to use the ApplicationRunner interface. The ApplicationRunner interface provides ApplicationArguments instead of raw string arrays for running methods.
ApplicationArguments is an interface available from boot 1.3 srping in the org.springFramework. boot package.
It provides the following ways to access parameters
String [] GetSourceArgs () | Provides unprocessed parameters that are passed to the application |
---|---|
The Set getOptionNames () | The names of all optional parameters preceded by ** “*– *-name = ‘stack trace’ |
The List getNonOptionArgs () | Returns an unprocessed non-optional argument. Without “* * * ** – * “parameter |
Boolean containsOption (string name) | Checks whether a name exists in the optional parameters |
List getOptionValues (string name) | Parameter values are given by name |
The method getOptionValues returns a list of values, because parameter values can be arrays, and because we can use the same key multiple times on the command line. Example: ** — name** = “stackTrace” — port = 8080 — name = “guru”
Example application runner as interface implementation
Let’s run the following program with the “status = RUNNING — mood = happy 10-20” parameter and see 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:[]
}
}
}
Copy the code
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: []
Copy the code
CommandLineRunner and ApplicationRunner have similar features, for example
- An exception in the run () method will abort application startup
- Multiple ApplicationRunners can be Ordered using the Ordered interface or the @Order notation
The most important thing to note is that commands are shared between CommandLineRunners and ApplicationRunners. This means that the execution order can be mixed between commandlinerRunner and applicationRunner.
3. Application events in Spring Boot
The Spring framework fires different events in different situations. It also fires many events during startup. We can use these events to execute code, for example, using ApplicationReadyEvent after a Spring Boot application has started.
If we don’t need command-line arguments, this is the best way to execute the code after the application starts.
@Component public class RunAfterStartup{ @EventListener(ApplicationReadyEvent.class) public void runAfterStartup() { System.out.println("Yaaah, I am running........" ); }Copy the code
Output:
Yaaah, I am running........
Copy the code
The most important event in spring boots is
- ApplicationContextInitializedEvent: preparing ApplicationContext and calls after ApplicationContextInitializers triggered but before loading the bean definition
- ApplicationPreparedEvent: Triggered after the bean definition is loaded
- ApplicationStartedEvent: Fired after the context is refreshed but before the command line is invoked and the application runs the program
- ApplicationReadyEvent: Triggered after any application and command line run program is called
- ApplicationFailedEvent: Triggered if an exception occurs during startup
Multiple ApplicationListeners can be created. It can be Ordered using the @Order annotation or Ordered interface.
This order is shared with other ApplicationListeners of the same type, but not with ApplicationRunners or CommandLineRunners.
4. @postconstruct annotations on methods
Markup methods can be annotated using @postconstruct annotations. Whenever this annotation marks a method, it is invoked immediately after dependency injection.
The @postconstruct method links to a specific class, so it only applies to class-specific code. Each class has only one method with a postConstruct annotation.
@Component public class PostContructImpl { public PostContructImpl() { System.out.println("PostContructImpl Constructor called"); } @PostConstruct public void runAfterObjectCreated() { System.out.println("PostContruct method called"); }}Copy the code
Output:
PostContructImpl Constructor called
postContruct method called
Copy the code
Note that if the class is marked lazy, it means that the class was created at request time. After that, the method marked with the @postconstruct annotation is executed.
Methods labeled with postConstruct annotations can have any name, but not any parameters. It must be invalid and cannot be static.
Note that the @Postconstruct annotation is part of the Java EE module and was marked deprecated in Java 9 and removed in Java 11. You can still use java.se.ee by adding it to your application.
5. InitializingBean interface
The InitializingBean solution works exactly like postConstruct annotations. Instead of using annotations, we must implement the InitializingBean interface. Then, we need to rewrite the afterPropertiesSet () method.
InitializingBean is org. Springframework. Beans. Part of the 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"); }}Copy the code
You must consider what happens if you use both the @Postconstruct annotation and the InitializingBean. In this case, the @PostConstruct method will be called before the afterPropertiesSet () method of the InitializingBean.
6. Init property of the @bean annotation
We can provide a method in the @bean annotation using the initMethod property. This method is called after the bean is initialized.
The methods provided in initMethod must be empty and cannot contain 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(); }}Copy the code
Output:
yooooooooo......... someone called me
Copy the code
If you have the InitializingBean implementation of the same class and the initMethod property of the @Bean annotation, the afterPropertiesSet method of the InitializingBean is called before initMethod.
Combine different approaches:
Finally, sometimes we may need to combine multiple options. They are then executed in the following order:
- builders
- Postconstruction method
- The afterPropertiesSet method
- Bean initialization method
- ApplicationStartedEvent
- ApplicationRunner or CommandLineRunner depends on the order
- ApplicationReadyEvent
Fast reading
- Once the application is launched, there are several ways to run the code
- We can use the CommandLineRunner or ApplicationRunner interface
- Use the ApplicationRunner interface to access parsed parameters instead of raw string arrays
- Spring Boot events execute code when the application starts
- Methods marked with the @postconstruct notation are executed after the object is initialized
- After the object is initialized, the afterPropertiesSet () method of the InitializingBean interface is called
- The @bean annotation has the attribute “initMethod” to provide the method to be called after the Bean is initialized
Related topics
- Spring Boot Project Configuration Guide