Cierre programáticamente la aplicación Spring Boot

109

¿Cómo puedo apagar programáticamente una aplicación Spring Boot sin terminar la VM ?

En otras obras, ¿qué es lo contrario de

new SpringApplication(Main.class).run(args);
Axel Fontaine
fuente
1
¡Buen punto! Llamar a close () en eso debería hacer el trabajo.
Axel Fontaine
¿Posible duplicado de Cómo cerrar una aplicación Spring Boot de manera correcta?
Anand Varkey Philips
@AnandVarkeyPhilips No, definitivamente no lo es. Este trata sobre una API, el otro trata sobre una forma en que las operaciones pueden hacerlo.
Axel Fontaine
De acuerdo ... Ese enlace de pregunta podría ayudar a otros. ¿Quieres que elimine el comentario anterior?
Anand Varkey Philips

Respuestas:

111

Cerrar un SpringApplicationbásicamente significa cerrar el subyacente ApplicationContext. El SpringApplication#run(String...)método te da eso ApplicationContextcomo un ConfigurableApplicationContext. Entonces puedes close()hacerlo tú mismo.

Por ejemplo,

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

Alternativamente, puede utilizar el static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...)método de ayuda para hacerlo por usted. Por ejemplo,

@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
fuente
1
Si uso ctx.close (); no hay necesidad de llamar a System.exit (n) al final, ¿verdad? context close () debería tener System.exit () dentro?
Denys
2
@Denys No, el contexto no sale del proceso de Java al cerrar. La salida en mi ejemplo solo demuestra cómo ExitCodeGeneratorse puede usar. Puede volver del mainmétodo para salir con gracia (código de salida 0).
Sotirios Delimanolis
78

En una aplicación de arranque de primavera, puede usar algo como esto

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
fuente
16
Voto a favor por demostrar que ApplicationContextse puede inyectar automáticamente en otros granos.
Anthony Chuinard
1
@snovelli ¿cómo invocar el método de inicio de apagado? initiateShutdown (x), x = 0?
StackOverFlow
Cuando hay un apagado condicional, esto se puede ejecutar. SpringApplication.run (..). Close () funcionará cuando el programa se complete.
Abubacker Siddik
por qué SpringApplication. (appContext, () -> returnCode); por qué no puede appContext.close (). cuál es la diferencia ?
Rams
@StackOverFlow Debe inyectar el bean donde lo necesite y luego pasar el código de retorno como sugirió (x = 0) si se está cerrando correctamente. Por ejemplo, podría inyectar el Shutdown Manager en un RestController y permitir el apagado remoto, o podría inyectarlo en un monitoreo de chequeo de estado que apagaría la JVM en caso de que falten los servicios
posteriores
36

Esto funciona, incluso hecho está impreso.

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

Entonces agregando .close()despuésrun()

Explicación:

public ConfigurableApplicationContext run(String... args)

Ejecute la aplicación Spring, creando y actualizando un nuevo ApplicationContext. Parámetros:

args - los argumentos de la aplicación (generalmente pasados ​​desde un método principal de Java)

Devuelve: un ApplicationContext en ejecución

y:

void close()Cierre este contexto de aplicación, liberando todos los recursos y bloqueos que pueda contener la implementación. Esto incluye destruir todos los beans singleton almacenados en caché. Nota: No invoca cerrar en un contexto principal; Los contextos principales tienen su propio ciclo de vida independiente.

Este método se puede llamar varias veces sin efectos secundarios: se ignorarán las llamadas de cierre posteriores en un contexto ya cerrado.

Entonces, básicamente, no cerrará el contexto principal, por eso la VM no se cierra.

ACV
fuente
2
Solo un recordatorio de que esta solución funciona para procesos de corta duración como lotes, pero no la use en aplicaciones Spring MVC. La aplicación simplemente se apaga después de arrancar.
Michael COLL
@MichaelCOLL la pregunta es sobre cómo cerrar programáticamente una aplicación de arranque de primavera independientemente del tipo. También funciona para Spring MVC
ACV
1
@ACV Tienes razón, funciona, funciona muy bien. Pero para una aplicación que debe permanecer activa (como la aplicación Spring MVC), creo que no es la buena forma de hacerlo. En mi caso, he usado SpringApplication.exit(appContext, () -> returnCode).
Michael COLL
1
¿A qué VM te refieres en tu última línea? Si está iniciando su aplicación Spring Boot con SpringApplication.run(MyApplication.class, args), entonces no hay un contexto principal. Solo hay un contexto, el contexto creado y devuelto por run, que luego inmediatamente close. @Michael tiene razón. Esto no funcionará para los programas que necesitan hacer algo después de que se inicialice el contexto Spring, que es la mayoría de los programas.
Salvador
@ Salvador JVM. Hay un contexto de padres. Aquí estamos hablando de cómo cerrar una aplicación Spring Boot. Normalmente no cierra las aplicaciones web de esta manera. Por lo tanto, este mecanismo se usa generalmente para aplicaciones de corta duración que hacen algo y luego deben detenerse. De forma predeterminada, Spring Boot seguirá ejecutándose incluso después de que haya terminado el procesamiento por lotes, por lo que es allí donde le gustaría usar este mecanismo.
ACV
3

En la aplicación puedes usar SpringApplication. Tiene un exit()método estático que toma dos argumentos: el ApplicationContexty un ExitCodeGenerator:

es decir, puede declarar este método:

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

Dentro de las pruebas de Integración puede lograrlo agregando una @DirtiesContextanotación a nivel de clase:

  • @DirtiesContext(classMode=ClassMode.AFTER_CLASS) - El ApplicationContext asociado se marcará como sucio después de la clase de prueba.
  • @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - El ApplicationContext asociado se marcará como sucio después de cada método de prueba en la clase.

es decir

@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 {
...
magico
fuente
Okay. ¿Dónde se supone que debo obtener ExecutorServiceSalirCodeGenerator? Si es un bean, ¿puede mostrar el código del fragmento de creación (y de qué clase se crea)? ¿En qué clase debe colocarse el método shutDown (ExecutorServiceSalirCodeGenerator exitCodeGenerator)?
Vlad G.
2

Esto asegurará que la aplicación SpringBoot se cierre correctamente y que los recursos se devuelvan al sistema operativo,

@Autowired
private ApplicationContext context;

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

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