12 votes

Meilleur moyen d'initialiser les beans dans le contexte Spring après le démarrage de l'application ?

J'ai besoin d'initialiser les beans dans le contexte Spring après le démarrage de mon application ; actuellement, j'initialise les beans dans une classe avec l'annotation @Configuration comme ça :

@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 ---
   }
}

Mais j'ai besoin d'initialiser certains haricots après le démarrage de l'application. ApplicationReadyEvent dans Spring et mettez le code pour initialiser les beans dans cette classe.

@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? ----
    }
}

Est-ce le meilleur moyen ? Ou existe-t-il d'autres meilleures solutions ?

34voto

Daniel C. Points 3075

Je vais énumérer d'autres approches afin d'initier les haricots, j'ai regroupé l'approche en Approche Standard et Approche Spring Boot.

Approche standard

  1. @PostConstruct il s'agit juste d'une annotation qui déclenche une méthode après la création du bean, elle n'autorise pas les paramètres d'entrée.
  2. @Bean(init-method="somInitMehotd") cette approche est totalement liée au cycle de vie du bean de Spring et est appelée après la création du bean, si vous utilisez une autre méthode avec @PostConstruct l'annotation @PostConstruct sera appelé en premier. Cette approche n'autorise pas les paramètres d'entrée.
  3. ApplicationListener Context Lifecycle : cette interface permet d'écouter les événements standard liés au cycle de vie du contexte, mais aussi des événements personnalisés. Par exemple : créer une classe MyAppListener et met en œuvre ApplicationListener<ContextRefreshedEvent> dans ce cas, le MyAppListener mettra en œuvre un onApplicationEvent qui reçoit un ContextRefreshedEvent

Approche de Spring Boot

  1. Les coureurs : Il existe deux interfaces très utiles CommandLineRunner y ApplicationRunner les deux s'exécutent après la création de l'ApplicationContext les deux permettent d'injecter des beans comme paramètres d'entrée.

  2. Écouteurs de Spring boot : Spring Application fournit des événements supplémentaires par rapport aux événements standards provenant du contexte de l'application. L'un de ces événements est ApplicationReadyEvent et il est déclenché lorsque l'application est prête à recevoir une demande. Afin d'écouter ces événements, il suffit d'implémenter la fonction ApplicationListener en utilisant ApplicationReadyEvent comme générique.

Voici l'exemple :

La classe MyBean a différentes méthodes qui seront appelées pour chaque approche listée ci-dessus, chaque méthode appellera une méthode print et cette méthode a un Thread.sleep afin de valider l'ordre dans lequel chaque listener est appelé.

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

Voici le ContextRefreshListener qui écoutera le ContextRefreshedEvent et le manipuler.

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

    }
}

Et c'est le BootListener qui recevra le ApplicationReadyEvent qui provient de Spring Application.

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

Et enfin l'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();
        };
    }

}

La sortie est :

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

Post init called La sortie provient de

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

Bean init called vient de la initMethod valeur

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

Context init called vient de ContextRefreshedEvent

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

}

Runner init called vient de CommandLineRunner

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

Boot init called vient de ApplicationReadyEvent

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

Tous les scénarios énumérés ont été déclenchés par Spring je n'ai appelé aucun des événements directement, ils ont tous été appelés par Spring Framework .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X