【问题标题】:Best way to initialize beans in Spring context after application started?应用程序启动后在 Spring 上下文中初始化 bean 的最佳方法?
【发布时间】:2017-08-18 03:14:12
【问题描述】:

我需要在我的应用程序启动后在 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 ---
   }
}

但是我需要在应用程序启动后初始化一些 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? ----
    }
}

这是最好的方法吗?还是有其他更好的解决方案?

【问题讨论】:

  • 试试@PostConstruct
  • @DanielC。添加 PostConstruct 注释时出现此错误:java.lang.IllegalStateException: Lifecycle method annotation requires a no-arg method:
  • 有一些链接可以帮助你达到你想要的结果。我希望它会有所帮助。 link1link2link3
  • 我只发布了一种使用“@PostConstruct”的方法
  • 请注意,spring 可能会创建多个上下文,因此@PostConstruct 可以被多次调用。

标签: java spring


【解决方案1】:

我将列举其他方法来初始化 bean,我将这些方法分为标准方法和 Spring Boot 方法。

标准方法

  1. @PostConstruct: 只是创建bean后触发方法的注解,不允许输入参数。
  2. @Bean(init-method="somInitMehotd"):此方法与 Spring bean 生命周期完全相关,在 bean 创建后调用,如果您使用带有 @PostConstruct 注释的其他方法,则将首先调用 @PostConstruct。这种方法不允许输入参数。
  3. ApplicationListener:该接口允许监听与上下文生命周期相关的标准事件,也可以监听自定义事件。例如:创建一个类MyAppListener 并实现ApplicationListener&lt;ContextRefreshedEvent&gt; 在这种情况下MyAppListener 将实现一个接收ContextRefreshedEventonApplicationEvent 方法

Spring Boot 方法

  1. 运行者:有两个非常有用的接口CommandLineRunnerApplicationRunner 在创建ApplicationContext 后都会运行它们都允许注入bean 作为输入参数。

  2. Spring 启动侦听器:Spring 应用程序提供了一些额外的事件,而不是来自应用程序上下文的标准事件。其中一个事件是ApplicationReadyEvent,它在应用程序准备好接收请求时触发。为了侦听此事件,只需使用ApplicationReadyEvent 作为通用实现ApplicationListener

示例如下:

MyBean 类有不同的方法,上面列出的每种方法都会被调用,每个方法都会调用一个 print 方法,并且该方法有一个 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();
        }
    }
}

这里是 ContextRefreshListener 类,它将监听 ContextRefreshedEvent 并处理它。

public class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        contextRefreshedEvent.getApplicationContext().getBean(MyBean.class).contextInit();

    }
}

BootListener 将接收来自 Spring 应用程序的 ApplicationReadyEvent

public class MyBootListener implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        applicationReadyEvent.getApplicationContext().getBean(MyBean.class).bootListenerInit();
    }
}

最后是 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();
        };
    }

}

输出是:

Post init called
Bean init called
Context init called
Runner init called 
Boot init called

Post init called 输出来自

@PostConstruct
public void init(){
    this.initByPostconstruct="Post init called";

Bean init called 来自initMethod

@Bean(name = "myBean", initMethod = "beanInit")
public MyBean getMyBean(){
    return new MyBean();
}
}

Context init called 来自ContextRefreshedEvent

public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
    contextRefreshedEvent.getApplicationContext().getBean(MyBean.class).contextInit();

}

Runner init called来自CommandLineRunner

@Bean
public CommandLineRunner getRunner(ApplicationContext ctx){
    return (args) -> {
        ctx.getBean(MyBean.class).runnerInit();
    };
}

Boot init called 来自ApplicationReadyEvent

   public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        applicationReadyEvent.getApplicationContext().getBean(MyBean.class).bootListenerInit();
    }

所有列出的场景都是Spring触发的,我没有直接调用任何事件,都是Spring Framework调用的。

【讨论】:

    猜你喜欢
    • 2011-01-23
    • 2017-01-31
    • 1970-01-01
    • 2010-09-23
    • 2015-02-07
    • 1970-01-01
    • 2015-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多