I need to initialize beans in the Spring context after my application has started; currently, I initialize beans in a class with annotation @Configuration like this:
@Configuration public class AppConfig { @Inject @Bean public BeanA init(param1, param2, etc...) { --- Code to construct bean A --- } @Inject @Bean public BeanB init(param1, param2, etc...) { --- Code to construct bean B --- } }
But some beans I need to initialize after application startup so my approach is create a class listen to ApplicationReadyEvent event in Spring and put the code to initialize beans in that class.
@Configuration class ApplicationStartingListener implements ApplicationListener<ApplicationReadyEvent>{ ---- Code to init bean here ---- @Override public void onApplicationEvent(ApplicationReadyEvent event) { --- If I put init bean code in here, is it correct? ---- } }
Is this the best way? Or there are some other better solutions?
In spring you can initialize a bean by having the applicationContext. xml invoke a constructor, or you can set properties on the bean.
Using Annotation: To provide the facility to the created bean to invoke custom init() method on the startup of a spring container and to invoke the custom destroy() method on closing the container, we need annotate init() method by @PostConstruct annotation and destroy() method by @PreDestroy annotation.
The default behavior for ApplicationContext implementations is to eagerly pre-instantiate all singleton beans at startup.
I will enumerate other approaches in order to init beans, I grouped the approach in Standard Approach and Spring Boot Approach.
Standard Approach
@PostConstruct
: it is just an annotation that triggers a method after bean is create, it doesn't allow input parameters.@Bean(init-method="somInitMehotd")
: this approach is totally related to Spring bean lifecycle and it is called after bean creation, if you are using another method with @PostConstruct
annotation, then the @PostConstruct
will be called first. This approach doesn't allow input parameters.ApplicationListener
: this interface allows to listen the standard events related to the Context Lifecycle, also it can listen customized events. For example: create a class MyAppListener
and implements ApplicationListener<ContextRefreshedEvent>
in this case the MyAppListener
will implement an onApplicationEvent
method that receives a ContextRefreshedEvent
Spring Boot Approach
The runners: There are two very useful interfaces CommandLineRunner
and ApplicationRunner
both of them will run after ApplicationContext is created both of them allows to inject beans as input parameters.
Spring boot listeners: Spring Application gives some additional events than the standards events that comes from the Application Context. One of the event is ApplicationReadyEvent
and it is fire when the application is ready to receive request. In order to listen this events just implements the ApplicationListener
using ApplicationReadyEvent
as generic.
Here is the example:
MyBean class has different methods that will be called for each approach listed above, every method will call a print method and that method has a Thread.sleep in order to validate the order that every listener is called.
import javax.annotation.PostConstruct; public class MyBean { private String myVar=""; public MyBean(){ } @PostConstruct public void postConstructInit(){ this.myVar="Post init called"; print(); } public void beanInit(){ this.myVar="Bean init called"; print(); } public void contextInit(){ this.myVar="Context init called"; print(); } public void runnerInit(){ this.myVar="Runner init called"; print(); } public void bootListenerInit(){ this.myVar="Boot init called"; print(); } public void print(){ System.out.println(this.myVar); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }
Here is the ContextRefreshListener
class that will listen the ContextRefreshedEvent
and handle it.
public class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { contextRefreshedEvent.getApplicationContext().getBean(MyBean.class).contextInit(); } }
And it is the BootListener
that will receive the ApplicationReadyEvent
that comes from Spring Application.
public class MyBootListener implements ApplicationListener<ApplicationReadyEvent> { @Override public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { applicationReadyEvent.getApplicationContext().getBean(MyBean.class).bootListenerInit(); } }
And finally the Spring Boot Application
@SpringBootApplication public class StackoverflowBootApplication { public static void main(String[] args) { SpringApplication.run(StackoverflowBootApplication.class, args); } @Bean(name = "myBean", initMethod = "beanInit") public MyBean getMyBean(){ return new MyBean(); } @Bean public ContextRefreshListener getContextRefreshedListener(){return new ContextRefreshListener();} @Bean public MyBootListener getBootListener(){return new MyBootListener();} @Bean public CommandLineRunner getRunner(ApplicationContext ctx){ return (args) -> { ctx.getBean(MyBean.class).runnerInit(); }; } }
The output is:
Post init called Bean init called Context init called Runner init called Boot init called
Post init called
output comes from
@PostConstruct public void init(){ this.initByPostconstruct="Post init called";
Bean init called
comes from the initMethod
value
@Bean(name = "myBean", initMethod = "beanInit") public MyBean getMyBean(){ return new MyBean(); } }
Context init called
comes from ContextRefreshedEvent
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { contextRefreshedEvent.getApplicationContext().getBean(MyBean.class).contextInit(); }
Runner init called
comes from CommandLineRunner
@Bean public CommandLineRunner getRunner(ApplicationContext ctx){ return (args) -> { ctx.getBean(MyBean.class).runnerInit(); }; }
Boot init called
comes from ApplicationReadyEvent
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { applicationReadyEvent.getApplicationContext().getBean(MyBean.class).bootListenerInit(); }
All the listed scenarios were triggered by Spring
, I didi'nt call any of the events directly, all of them were called by Spring Framework
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With