188 votes

Exécution de la méthode au démarrage dans Spring

Existe-t-il une fonctionnalité de 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 de définir une méthode avec @Scheduled et elle s'exécute juste après le démarrage, mais ensuite elle s'exécutera périodiquement.

2 votes

Quel est le truc avec @Scheduled ? c'est exactement ce que je veux !

201voto

skaffman Points 197885

Si par "démarrage de l'application" vous voulez dire "démarrage du contexte de l'application", alors oui, il y a plusieurs façons de procéder le plus simple (pour les beans singletons, en tout cas) étant d'annoter votre méthode avec @PostConstruct . Jetez un coup d'œil au lien pour voir les autres options, mais en résumé, elles sont les suivantes :

  • Méthodes annotées avec @PostConstruct
  • afterPropertiesSet() tel que défini par le InitializingBean interface de rappel
  • Une méthode init() configurée de manière personnalisée

Techniquement, il s'agit de crochets dans le haricot plutôt que le cycle de vie du contexte, mais dans 99 % des cas, les deux sont équivalents.

Si vous avez besoin de vous brancher spécifiquement sur le contexte de démarrage/arrêt, alors vous pouvez mettre en œuvre la Lifecycle interface à la place, mais c'est probablement inutile.

7 votes

Après de nombreuses recherches, je n'ai toujours pas vu d'implémentation de Lifecycle ou de SmartLifecycle. Je sais que ce sujet date d'un an, mais skaffman, si vous avez quelque chose à poster, ce serait très apprécié.

4 votes

Les méthodes ci-dessus sont invoquées avant que le contexte de l'application ne soit entièrement créé (par exemple, avant que la démarcation de la transaction ne soit établie).

0 votes

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/H‌​ome/jre/lib/rt.jar

109voto

Stefan Haberl Points 764

Cela se fait facilement avec un ApplicationListener . J'ai réussi à le faire fonctionner en écoutant les chansons de 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'application 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, il suffit de conserver un certain état dans votre composant.

UPDATE

À partir de la version 4.2+ de Spring, vous pouvez également utiliser la fonction @EventListener 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 votes

Cela a fonctionné pour moi aussi - parfait pour une initialisation unique sans haricot.

9 votes

N.B. pour ceux qui sont tentés d'utiliser ContextStartedEvent il est plus difficile d'ajouter l'écouteur avant que l'événement ne se déclenche.

2 votes

Comment appeler un référentiel JPA @Autowired dans un événement ? Le référentiel est nul.

40voto

bphilipnyc Points 70

Dans Spring 4.2+, vous pouvez maintenant simplement faire :

@Component
class StartupHousekeeper {

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

0 votes

Est-il garanti que ce listener n'est invoqué qu'une seule fois après le démarrage ?

0 votes

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

13voto

Zombies Points 5977

Si vous utilisez un pied à ressort, c'est la meilleure réponse.

J'ai l'impression que @PostConstruct et autres diverses interjections du cycle de vie sont des moyens détournés. Elles peuvent conduire directement à des problèmes d'exécution ou provoquer des défauts moins évidents dus à des événements inattendus du cycle de vie du bean/contexte. Pourquoi ne pas invoquer directement votre bean en utilisant Java ? Vous invoquez toujours le bean à la "manière Spring" (par exemple, via le proxy AoP de Spring). Et le mieux, c'est que c'est du simple Java, il n'y a pas plus simple que ça. Pas besoin d'écouteurs de contexte ou d'ordonnanceurs bizarres.

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

6 votes

C'est une bonne idée en général, mais lorsque vous démarrez le contexte de votre application Spring à partir d'un test d'intégration, main n'est jamais exécuté !

0 votes

@JonasGeiregat : De plus, il y a d'autres scénarios dans lesquels il n'y a pas de main() du tout, par exemple lorsqu'on utilise un cadre d'application (par exemple, JavaServer Faces).

9voto

encrest Points 1397

Pour les utilisateurs de Java 1.8 qui reçoivent un avertissement lorsqu'ils essaient de faire référence à l'annotation @PostConstruct, j'ai fini par me servir de 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");
    }

}

0 votes

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