web-dev-qa-db-fra.com

Exécuter la méthode au démarrage au printemps

Existe-t-il une fonctionnalité Spring 3 permettant d'exécuter certaines méthodes lorsque l'application démarre pour la première fois? Je sais que je peux faire l'astuce en définissant une méthode avec l'annotation @Scheduled et qu'elle s'exécute juste après le démarrage, mais elle s'exécutera périodiquement.

157
Javi

Si par "démarrage de l'application" vous voulez dire "démarrage du contexte de l'application", alors oui, il y a de nombreuses façons de faire cela , le plus simple (pour les beans singletons de toute façon) consiste à annoter votre méthode avec @PostConstruct. Regardez le lien pour voir les autres options, mais en résumé, elles sont:

  • Méthodes annotées avec @PostConstruct
  • afterPropertiesSet() tel que défini par l'interface de rappel InitializingBean
  • Une méthode personnalisée init ()

Techniquement, ce sont des points d'ancrage dans le cycle de vie bean, plutôt que dans le contexte, mais dans 99% des cas, les deux sont équivalents.

Si vous devez vous attacher spécifiquement au démarrage/à l'arrêt du contexte, vous pouvez implémenter l'interface Lifecycle à la place, mais c'est probablement inutile.

172
skaffman

Cela se fait facilement avec un ApplicationListener. Je suis au travail en écoutant la ContextRefreshedEvent de Spring:

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class StartupHousekeeper implements ApplicationListener<ContextRefreshedEvent> {

  @Override
  public void onApplicationEvent(final ContextRefreshedEvent event) {
    // do whatever you need here 
  }
}

Les écouteurs d'application fonctionnent de manière synchrone au printemps. Si vous voulez vous assurer que votre code n'est exécuté qu'une seule fois, conservez simplement un état dans votre composant.

METTRE &AGRAVE; JOUR

À partir de Spring 4.2+, vous pouvez également utiliser l'annotation @EventListener pour observer la ContextRefreshedEvent (merci à @bphilipnyc de l'avoir signalé):

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class StartupHousekeeper {

  @EventListener(ContextRefreshedEvent.class)
  public void contextRefreshedEvent() {
    // do whatever you need here 
  }
}
94
Stefan Haberl

Au printemps 4.2+, vous pouvez maintenant faire simplement:

@Component
class StartupHousekeeper {

    @EventListener(ContextRefreshedEvent.class)
    void contextRefreshedEvent() {
        //do whatever
    }
}
32
bphilipnyc

Pour les utilisateurs de Java 1.8 qui reçoivent un avertissement lorsqu'ils tentent de référencer l'annotation @PostConstruct, je me suis plutôt retrouvé à greffer l'annotation @Scheduled que vous pouvez effectuer si vous avez déjà un travail @Scheduled avec fixedRate ou fixedDelay.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@EnableScheduling
@Component
public class ScheduledTasks {

private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTasks.class);

private static boolean needToRunStartupMethod = true;

    @Scheduled(fixedRate = 3600000)
    public void keepAlive() {
        //log "alive" every hour for sanity checks
        LOGGER.debug("alive");
        if (needToRunStartupMethod) {
            runOnceOnlyOnStartup();
            needToRunStartupMethod = false;
        }
    }

    public void runOnceOnlyOnStartup() {
        LOGGER.debug("running startup job");
    }

}
9
encrest

Si vous utilisez Spring-Boot, c'est la meilleure réponse.

Je pense que @PostConstruct et d'autres interjections de cycles de vie variés sont des moyens détournés. Celles-ci peuvent entraîner directement des problèmes d'exécution ou entraîner des défauts moins qu'évidents en raison d'événements de cycle de vie de bean/contexte inattendus. Pourquoi ne pas simplement invoquer directement votre bean à l'aide de Java pur? Vous invoquez toujours le haricot à la «source printanière» (par exemple, via le proxy AoP du printemps). Et le meilleur de tous, il est clair Java, ne peut pas être plus simple que cela. Pas besoin d'écoute de contexte ou d'ordonnanceurs impairs.

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext app = SpringApplication.run(DemoApplication.class, args);

        MyBean myBean = (MyBean)app.getBean("myBean");

        myBean.invokeMyEntryPoint();
    }
}
9
Zombies

Nous avons étendu org.springframework.web.context.ContextLoaderListener pour imprimer quelque chose au début du contexte.

public class ContextLoaderListener extends org.springframework.web.context.ContextLoaderListener
{
    private static final Logger logger = LoggerFactory.getLogger( ContextLoaderListener.class );

    public ContextLoaderListener()
    {
        logger.info( "Starting application..." );
    }
}

Configurez la sous-classe puis dans web.xml:

<listener>
    <listener-class>
        com.mycomp.myapp.web.context.ContextLoaderListener
    </listener-class>
</listener>
7
Wim Deblauwe

Attention, ceci n’est conseillé que si votre méthode runOnceOnStartup dépend de contexte de printemps totalement initialisé. Par exemple: vous voulez appeler un dao avec démarcation des transactions

Vous pouvez également utiliser une méthode planifiée avec fixedDelay très élevé

@Scheduled(fixedDelay = Long.MAX_VALUE)
public void runOnceOnStartup() {
    dosomething();
}

Cela a l'avantage que toute l'application est câblée (Transactions, Dao, ...)

vu dans Planification des tâches à exécuter une fois, à l'aide de l'espace de noms des tâches Spring

3
Joram

Avec SpringBoot, nous pouvons exécuter une méthode au démarrage via l'annotation @EventListener

@Component
public class LoadDataOnStartUp
{   
    @EventListener(ApplicationReadyEvent.class)
    public void loadData()
    {
        // do something
    }
}

3

Publié une autre solution qui implémente WebApplicationInitializer et est appelée bien avant l'instanciation d'un bean printemps, au cas où quelqu'un aurait ce cas d'utilisation

Initialise les paramètres régionaux et le fuseau horaire par défaut avec la configuration Spring

1
kisna

Si vous souhaitez configurer un bean avant que votre application ne soit complètement exécutée, vous pouvez utiliser @Autowired:

@Autowired
private void configureBean(MyBean: bean) {
    bean.setConfiguration(myConfiguration);
}
0
Cory Klein
AppStartListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if(event instanceof ApplicationReadyEvent){
            System.out.print("ciao");

        }
    }
}
0
dnocode

Pour un fichier StartupHousekeeper.Java situé dans le package com.app.startup,

Faites ceci dans StartupHousekeeper.Java:

@Component
public class StartupHousekeeper {

  @EventListener(ContextRefreshedEvent.class)
  public void keepHouse() {
    System.out.println("This prints at startup.");
  }
}

Et faites ceci dans myDispatcher-servlet.Java:

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <mvc:annotation-driven />
    <context:component-scan base-package="com.app.startup" />

</beans>
0
Cameron Hudson

Vous pouvez utiliser @EventListener sur votre composant, qui sera appelé après le démarrage du serveur et l'initialisation de tous les beans.

@EventListener
public void onApplicationEvent(ContextClosedEvent event) {

}
0
krmanish007