Java 应用程序启动后在 Spring 上下文中初始化 bean 的最佳方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/45747933/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to initialize beans in Spring context after application started?
提问by Thong Vo
I need to initialize beans in the Spring context after my application has started; currently, I initialize beans in a class with annotation @Configurationlike this:
我的应用程序启动后,我需要在 Spring 上下文中初始化 bean;目前,我在类中使用注释@Configuration初始化 bean,如下所示:
@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 ApplicationReadyEventevent in Spring and put the code to initialize beans in that class.
但是我需要在应用程序启动后初始化一些 bean,所以我的方法是在 Spring 中创建一个类来监听ApplicationReadyEvent事件,并将代码放在该类中以初始化 bean。
@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?
这是最好的方法吗?或者还有其他更好的解决方案?
回答by Daniel C.
I will enumerate other approaches in order to init beans, I grouped the approach in Standard Approach and Spring Boot Approach.
我将列举其他方法来初始化 bean,我将这些方法归入标准方法和 Spring Boot 方法中。
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 classMyAppListener
and implementsApplicationListener<ContextRefreshedEvent>
in this case theMyAppListener
will implement anonApplicationEvent
method that receives aContextRefreshedEvent
@PostConstruct
: 只是一个注解,在bean创建后触发一个方法,它不允许输入参数。@Bean(init-method="somInitMehotd")
: 这种方式与Spring bean的生命周期完全相关,在bean创建之后调用,如果你使用的是其他带@PostConstruct
注解的方法,那么@PostConstruct
会先调用。这种方法不允许输入参数。ApplicationListener
:该接口允许监听与上下文生命周期相关的标准事件,也可以监听自定义事件。例如:创建一个类MyAppListener
并实现ApplicationListener<ContextRefreshedEvent>
在这种情况下MyAppListener
将实现一个onApplicationEvent
方法,该方法接收一个ContextRefreshedEvent
Spring Boot Approach
Spring Boot 方法
The runners: There are two very useful interfaces
CommandLineRunner
andApplicationRunner
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 theApplicationListener
usingApplicationReadyEvent
as generic.
该参赛者:有两个非常有用的接口
CommandLineRunner
,并ApplicationRunner
在创建后的ApplicationContext他们都可以注入豆作为输入参数他们都将运行。Spring 启动侦听器:除了来自应用程序上下文的标准事件之外,Spring 应用程序还提供了一些额外的事件。事件之一是
ApplicationReadyEvent
当应用程序准备好接收请求时触发。为了侦听此事件,只需实现ApplicationListener
usingApplicationReadyEvent
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.
MyBean 类具有不同的方法,这些方法将针对上面列出的每种方法调用,每个方法都将调用一个打印方法,并且该方法具有一个 Thread.sleep 以验证调用每个侦听器的顺序。
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.
这是ContextRefreshListener
将侦听ContextRefreshedEvent
并处理它的类。
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.
它是BootListener
将接收ApplicationReadyEvent
的是来自Spring应用程序。
public class MyBootListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
applicationReadyEvent.getApplicationContext().getBean(MyBean.class).bootListenerInit();
}
}
And finally the Spring Boot Application
最后是 Spring Boot 应用程序
@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
Post init called
输出来自
@PostConstruct
public void init(){
this.initByPostconstruct="Post init called";
Bean init called
comes from the initMethod
value
Bean init called
来自initMethod
价值
@Bean(name = "myBean", initMethod = "beanInit")
public MyBean getMyBean(){
return new MyBean();
}
}
Context init called
comes from ContextRefreshedEvent
Context init called
来自 ContextRefreshedEvent
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
contextRefreshedEvent.getApplicationContext().getBean(MyBean.class).contextInit();
}
Runner init called
comes from CommandLineRunner
Runner init called
来自 CommandLineRunner
@Bean
public CommandLineRunner getRunner(ApplicationContext ctx){
return (args) -> {
ctx.getBean(MyBean.class).runnerInit();
};
}
Boot init called
comes from ApplicationReadyEvent
Boot init called
来自 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
.
所有列出的场景都是由 触发的Spring
,我没有直接调用任何事件,它们都是由Spring Framework
.