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


176

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


1
quel est le truc avec @Scheduled? c'est exactement ce que je veux!
chrismarx

Réponses:


185

Si par "démarrage de l'application" vous entendez "démarrage du contexte de l'application", alors oui, il existe de nombreuses façons de le faire , la plus simple (pour les beans singletons, en tout cas) étant d'annoter votre méthode avec @PostConstruct. Jetez un œil au lien pour voir les autres options, mais en résumé, ce sont:

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

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

Si vous avez besoin de vous connecter spécifiquement au démarrage / arrêt du contexte, vous pouvez implémenter l' Lifecycleinterface à la place, mais ce n'est probablement pas nécessaire.


7
Je n'ai pas encore vu d'implémentation de Lifecycle ou SmartLifecycle après pas mal de recherches. Je sais que c'est un an, mais skaffman si vous avez quelque chose que vous pouvez poster, ce serait très apprécié.

4
Les méthodes ci-dessus sont appelées avant que tout le contexte d'application n'ait été créé (par exemple, / avant / la démarcation de transaction a été configurée).
Hans Westerbeek

Je reçois un étrange avertissement en essayant d'utiliser @PostConstruct en java 1.8:Access restriction: The type PostConstruct is not accessible due to restriction on required library /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar
encrest le

2
Il existe des cas importants où le cycle de vie du bean et du contexte est très différent. Comme @HansWesterbeek l'a noté, un bean peut être configuré avant que le contexte dont il dépend ne soit complètement prêt. Dans ma situation, un bean dépendait de JMS - il était entièrement construit, donc sa @PostConstructméthode a été appelée, mais l'infrastructure JMS dont il dépendait indirectement n'était pas encore complètement câblée (et étant Spring, tout a échoué en silence). Lors du passage à @EventListener(ApplicationReadyEvent.class)tout fonctionnait ( ApplicationReadyEventSpring Boot est-il spécifique à vanilla Spring, voir la réponse de Stefan).
George Hawkins

@Skaffman: que se passe-t-il si mon haricot n'est référencé par aucun haricot et que je veux initialiser le haricot sans être utilisé nulle part
Sagar Kharab

104

Cela se fait facilement avec un fichier ApplicationListener. J'ai fait travailler ça en écoutant Spring ContextRefreshedEvent:

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'applications s'exécutent de manière synchrone dans Spring. Si vous voulez vous assurer que votre code n'est exécuté qu'une seule fois, gardez simplement un état dans votre composant.

METTRE À JOUR

À partir de Spring 4.2+, vous pouvez également utiliser l' @EventListenerannotation pour observer le ContextRefreshedEvent(merci à @bphilipnyc pour 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 
  }
}

1
Cela a fonctionné pour moi aussi - parfait pour une initialisation non-bean ponctuelle.
Rory Hunter

9
NB pour ceux qui sont tentés d'utiliser à la ContextStartedEventplace, il est plus difficile d'ajouter l'auditeur avant le déclenchement de l'événement.
OrangeDog

2
Comment appeler un référentiel JPA @Autowired en événement? le référentiel est nul.
e-info128

Ne travaille pas pour moi. J'utilise spring mvc 3. Cette méthode onApplicationEvent (___) n'est pas appelée lorsque l'application démarre. De l'aide. Voici mon code @Component public class AppStartListener implémente ApplicationListener <ContextRefreshedEvent> {public void onApplicationEvent (événement ContextRefreshedEvent final) {System.out.println ("\ n \ n \ nInside on application event"); }}
Vishwas Tyagi

@VishwasTyagi Comment démarrez-vous votre conteneur? Êtes-vous sûr que votre AppStartListener fait partie de votre analyse des composants?
Stefan Haberl

38

Dans Spring 4.2+, vous pouvez maintenant simplement faire:

@Component
class StartupHousekeeper {

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

Est-il garanti que cet écouteur n'appelle qu'une seule fois après le démarrage?
gstackoverflow

Non, voyez ma réponse ci-dessus. Gardez un état dans votre auditeur pour vérifier s'il s'exécute pour la première fois
Stefan Haberl

13

Si vous utilisez des bottes à ressort, c'est la meilleure réponse.

Je pense que cela @PostConstructet d'autres interjections du cycle de vie sont des voies rondes. Celles-ci peuvent conduire directement à des problèmes d'exécution ou provoquer des défauts moins qu'évidents dus à des événements de cycle de vie bean / contexte inattendus. Pourquoi ne pas simplement appeler directement votre bean en utilisant Java brut? Vous invoquez toujours le bean de la «manière du ressort» (par exemple: via le proxy Spring AoP). Et le meilleur de tous, c'est du java simple, ne peut pas être plus simple que cela. Pas besoin d'écouteurs de contexte ou de programmateurs étranges.

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

5
C'est une bonne idée en général, mais lors du démarrage de votre contexte d'application Spring à partir d'un test d'intégration, main n'est jamais exécuté!
Jonas Geiregat

@JonasGeiregat: De plus, il existe d'autres scénarios où il n'y en a pas main()du tout, par exemple lors de l'utilisation d'un framework d'application (par exemple JavaServer Faces).
sleske

9

Pour les utilisateurs de Java 1.8 qui reçoivent un avertissement lorsqu'ils essaient de référencer l'annotation @PostConstruct, j'ai fini par utiliser l'annotation @Scheduled, ce que vous pouvez faire 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");
    }

}


7

Ce que nous avons fait a été d'étendre org.springframework.web.context.ContextLoaderListenerpour imprimer quelque chose lorsque le contexte démarre.

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 ensuite la sous-classe dans web.xml:

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

7

Avec SpringBoot, nous pouvons exécuter une méthode au démarrage via une @EventListenerannotation

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


4

Attention, cela n'est conseillé que si votre runOnceOnStartupméthode dépend d'un contexte de ressort entièrement initialisé. Par exemple: vous souhaitez appeler un DAO avec une démarcation de transaction

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

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

Cela présente 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 de tâches Spring


Je ne vois aucun avantage à utiliser @PostConstruct?
Wim Deblauwe

@WimDeblauwe dépend de ce que vous voulez faire dans dosomething () pour appeler un dao Autowired avec une démarcation Trasaction nécessite que tout le contexte soit démarré, pas seulement ce bean
Joram

5
@WimDeblauwe La méthode '@PostConstruct' se déclenche lorsque le bean est initialisé, tout le contexte peut ne pas être prêt (par exemple, la gestion des transactions)
Joram

C'est plus élégant imo que post-construction ou toute interface ou événement
aliopi


1
AppStartListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if(event instanceof ApplicationReadyEvent){
            System.out.print("ciao");

        }
    }
}

2
ApplicationReadyEvent est au printemps pas au printemps 3
John Mercier

0

Si vous souhaitez configurer un bean avant que votre application ne s'exécute complètement, vous pouvez utiliser @Autowired:

@Autowired
private void configureBean(MyBean: bean) {
    bean.setConfiguration(myConfiguration);
}

0

Vous pouvez utiliser @EventListenersur 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

Pour un fichier StartupHousekeeper.javasitué 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-le en myDispatcher-servlet.java:

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

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

</beans>
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.