Arrêter l'application Spring Boot par programme

109

Comment puis-je arrêter par programme une application Spring Boot sans arrêter la machine virtuelle ?

Dans d'autres œuvres, quel est le contraire de

new SpringApplication(Main.class).run(args);
Axel Fontaine
la source
1
Bon point! Appeler close () à ce sujet devrait faire l'affaire.
Axel Fontaine
@AnandVarkeyPhilips Non, ce n'est certainement pas le cas. Celui-ci concerne une API, l'autre un moyen pour les opérations de le faire.
Axel Fontaine
D'accord. Ce lien de question pourrait aider les autres. Voulez-vous que je supprime le commentaire ci-dessus?
Anand Varkey Philips

Réponses:

111

Fermer un SpringApplicationsignifie essentiellement fermer le sous-jacent ApplicationContext. La SpringApplication#run(String...)méthode vous donne cela ApplicationContextsous forme de fichier ConfigurableApplicationContext. Vous pouvez ensuite le close()faire vous-même.

Par exemple,

@SpringBootApplication
public class Example {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
        // ...determine it's time to shut down...
        ctx.close();
    }
}

Vous pouvez également utiliser la static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...)méthode d'assistance pour le faire pour vous. Par exemple,

@SpringBootApplication
public class Example {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
        // ...determine it's time to stop...
        int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
            @Override
            public int getExitCode() {
                // no errors
                return 0;
            }
        });

        // or shortened to
        // int exitCode = SpringApplication.exit(ctx, () -> 0);

        System.exit(exitCode);
    }
}
Sotirios Delimanolis
la source
1
Si j'utilise ctx.close (); il n'est pas nécessaire d'appeler System.exit (n) à la fin, non? context close () devrait avoir System.exit () à l'intérieur?
Denys
2
@Denys Non, le contexte ne quitte pas le processus java à la fermeture. La sortie dans mon exemple montre simplement comment le ExitCodeGeneratorpeut être utilisé. Vous pouvez simplement revenir de la mainméthode pour quitter gracieusement (code de sortie 0).
Sotirios Delimanolis
78

Dans une application de démarrage au printemps, vous pouvez utiliser quelque chose comme ça

ShutdownManager.java

import org.springframework.context.ApplicationContext;
import org.springframework.boot.SpringApplication;

@Component
class ShutdownManager{

    @Autowired
    private ApplicationContext appContext;

    public void initiateShutdown(int returnCode){
        SpringApplication.exit(appContext, () -> returnCode);
    }
}
snovelli
la source
16
Votez pour montrer que le ApplicationContextpeut être automatiquement injecté dans d'autres beans.
Anthony Chuinard
1
@snovelli comment invoquer la méthode de démarrage de l'arrêt? initiateShutdown (x), x = 0?
StackOverFlow
Lorsqu'il y a un arrêt conditionnel, cela peut être exécuté. SpringApplication.run (..). Close () fonctionnera à la fin du programme.
Abubacker Siddik
pourquoi SpringApplication. (appContext, () -> returnCode); pourquoi ne peut pas appContext.close (). Quelle est la différence ?
Rams du
@StackOverFlow Vous devez injecter le bean là où vous en avez besoin, puis transmettre le code de retour comme vous l'avez suggéré (x = 0) s'il s'arrête correctement. Par exemple, vous pouvez injecter Shutdown Manager dans un RestController et autoriser l'arrêt à distance, ou vous pouvez l'injecter dans un contrôle de santé qui arrêterait la JVM en cas de services en aval manquants
snovelli
36

Cela fonctionne, même fait est imprimé.

  SpringApplication.run(MyApplication.class, args).close();
  System.out.println("done");

Donc en ajoutant .close()aprèsrun()

Explication:

public ConfigurableApplicationContext run(String... args)

Exécutez l'application Spring, en créant et en actualisant un nouveau ApplicationContext. Paramètres:

args - les arguments de l'application (généralement passés d'une méthode principale Java)

Renvoie: un ApplicationContext en cours d'exécution

et:

void close()Fermez ce contexte d'application, libérant toutes les ressources et verrous que l'implémentation peut contenir. Cela inclut la destruction de tous les beans singleton mis en cache. Remarque: n'invoque pas close sur un contexte parent; les contextes parents ont leur propre cycle de vie indépendant.

Cette méthode peut être appelée plusieurs fois sans effets secondaires: les appels de fermeture suivants sur un contexte déjà fermé seront ignorés.

Donc, fondamentalement, cela ne fermera pas le contexte parent, c'est pourquoi la VM ne se ferme pas.

ACV
la source
2
Juste un rappel, cette solution fonctionne pour les processus de courte durée comme le lot, mais ne l'utilisez pas sur les applications Spring MVC. L'application s'arrête juste après le démarrage.
Michael COLL
@MichaelCOLL, la question est de savoir comment arrêter par programme une application Spring Boot, quel que soit son type. Cela fonctionne également pour Spring MVC
ACV
1
@ACV Vous avez raison ça marche, ça marche très bien. Mais pour une application qui doit rester active (comme l'application Spring MVC), je pense que ce n'est pas la bonne façon de le faire. Dans mon cas, j'ai utilisé SpringApplication.exit(appContext, () -> returnCode).
Michael COLL
1
De quelle VM parlez-vous dans votre dernière ligne? Si vous démarrez votre application Spring Boot avec SpringApplication.run(MyApplication.class, args), il n'y a pas de contexte parent. Il n'y a qu'un seul contexte, le contexte créé et renvoyé par run, que vous ensuite immédiatement close. @Michael a raison. Cela ne fonctionnera pas pour les programmes qui ont besoin de faire quoi que ce soit après l'initialisation du contexte Spring, qui est la plupart des programmes.
Sauveur le
@Savior JVM. Il y a un contexte parent. Nous parlons ici de la façon d'arrêter une application de démarrage Spring. Normalement, vous ne fermez pas les applications Web de cette façon. Donc, ce mécanisme est généralement utilisé pour les applications de courte durée qui font quelque chose puis doivent s'arrêter. Par défaut, Spring boot continuera à fonctionner même après avoir terminé votre traitement par lots, c'est donc là que vous voudrez utiliser ce mécanisme.
ACV
3

Dans l'application, vous pouvez utiliser SpringApplication. Cela a une exit()méthode statique qui prend deux arguments: le ApplicationContextet un ExitCodeGenerator:

c'est à dire que vous pouvez déclarer cette méthode:

@Autowired
public void shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator) {
    SpringApplication.exit(applicationContext, exitCodeGenerator);
}

Dans les tests d'intégration, vous pouvez y parvenir en ajoutant une @DirtiesContextannotation au niveau de la classe:

  • @DirtiesContext(classMode=ClassMode.AFTER_CLASS) - Le ApplicationContext associé sera marqué comme sale après la classe de test.
  • @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - Le ApplicationContext associé sera marqué comme sale après chaque méthode de test de la classe.

c'est à dire

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {Application.class},
    webEnvironment= SpringBootTest.WebEnvironment.DEFINED_PORT, properties = {"server.port:0"})
@DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ApplicationIT {
...
magiccrafter
la source
D'accord. Où suis-je censé obtenir ExecutorServiceExitCodeGenerator? S'il s'agit d'un bean, pouvez-vous afficher le code de l'extrait de création (et à partir de quelle classe il est créé)? Dans quelle classe la méthode shutDown (ExecutorServiceExitCodeGenerator exitCodeGenerator) doit-elle être placée?
Vlad G.
2

Cela garantira que l'application SpringBoot est fermée correctement et que les ressources sont libérées vers le système d'exploitation,

@Autowired
private ApplicationContext context;

@GetMapping("/shutdown-app")
public void shutdownApp() {

    int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
    System.exit(exitCode);
}
Samim Aftab Ahmed
la source