Desligue programaticamente o aplicativo Spring Boot

109

Como posso desligar programaticamente um aplicativo Spring Boot sem encerrar a VM ?

Em outras obras, o que é o oposto de

new SpringApplication(Main.class).run(args);
Axel Fontaine
fonte
1
Bom ponto! Chamar close () nisso deve fazer o trabalho.
Axel Fontaine
Possível duplicata de Como desligar um aplicativo Spring Boot de maneira correta?
Anand Varkey Philips
@AnandVarkeyPhilips Não, definitivamente não é. Este é sobre uma API, o outro é sobre uma maneira de os ops fazerem isso.
Axel Fontaine
Ok .. Esse link de pergunta pode ajudar outras pessoas. Você quer que eu exclua o comentário acima?
Anand Varkey Philips de

Respostas:

111

Fechar um SpringApplicationsignifica basicamente fechar o subjacente ApplicationContext. O SpringApplication#run(String...)método fornece isso ApplicationContextcomo um ConfigurableApplicationContext. Você pode então fazer close()isso sozinho.

Por exemplo,

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

Como alternativa, você pode usar o static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...)método auxiliar para fazer isso por você. Por exemplo,

@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
fonte
1
Se eu usar ctx.close (); não há necessidade de chamar System.exit (n) no final, certo? O contexto close () deve ter System.exit () dentro?
Denys
2
@Denys Não, o contexto não sai do processo java no fechamento. A saída no meu exemplo apenas demonstra como o ExitCodeGeneratorpode ser usado. Você pode simplesmente retornar do mainmétodo para sair normalmente (código de saída 0).
Sotirios Delimanolis
78

Em um aplicativo de inicialização de primavera, você pode usar algo assim

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
fonte
16
Votos positivos por mostrar que o ApplicationContextpode ser injetado automaticamente em outros grãos.
Anthony Chuinard
1
@snovelli como invocar o método de desligamento inicial? beginteShutdown (x), x = 0?
StackOverFlow
Quando houver desligamento condicional, isso pode ser executado. SpringApplication.run (..). Close () fará conforme o programa é concluído.
Abubacker Siddik
porque SpringApplication. (appContext, () -> returnCode); por que não pode appContext.close (). Qual é a diferença ?
Rams
@StackOverFlow Você precisa injetar o bean onde precisa e então passar o código de retorno conforme sugerido (x = 0) se ele estiver sendo encerrado corretamente. Por exemplo, você pode injetar o Shutdown Manager em um RestController e permitir o desligamento remoto ou pode injetá-lo em um monitoramento de verificação de integridade que desligaria a JVM no caso de serviços downstream ausentes
snovelli 01 de
36

Isso funciona, mesmo quando feito é impresso.

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

Então, adicionando .close()depoisrun()

Explicação:

public ConfigurableApplicationContext run(String... args)

Execute o aplicativo Spring, criando e atualizando um novo ApplicationContext. Parâmetros:

args - os argumentos do aplicativo (geralmente passados ​​de um método principal Java)

Retorna: um ApplicationContext em execução

e:

void close()Feche este contexto de aplicativo, liberando todos os recursos e bloqueios que a implementação possa conter. Isso inclui destruir todos os beans singleton armazenados em cache. Nota: Não invoca o fechamento em um contexto pai; contextos pais têm seu próprio ciclo de vida independente.

Este método pode ser chamado várias vezes sem efeitos colaterais: As chamadas de fechamento subsequentes em um contexto já fechado serão ignoradas.

Então, basicamente, não fechará o contexto pai, é por isso que a VM não fecha.

ACV
fonte
2
Apenas um lembrete de que essa solução funciona para processos de curta duração, como lote, mas não use isso em aplicativos Spring MVC. O aplicativo é encerrado após a inicialização.
Michael COLL
@MichaelCOLL a questão é sobre como desligar programaticamente um aplicativo de inicialização de mola, independentemente do tipo. Também funciona para Spring MVC
ACV
1
@ACV Você está certo, funciona, funciona muito bem. Mas para um aplicativo que deve permanecer ativo (como o aplicativo Spring MVC), acho que não é a boa maneira de fazer isso. No meu caso, eu usei SpringApplication.exit(appContext, () -> returnCode).
Michael COLL
1
A qual VM você está se referindo em sua última linha? Se você está iniciando seu aplicativo Spring Boot com SpringApplication.run(MyApplication.class, args), não há contexto pai. Há apenas um contexto, o contexto criado e retornado run, que você imediatamente close. @Michael está certo. Isso não funcionará para programas que precisam fazer qualquer coisa depois que o contexto Spring é inicializado, que é a maioria dos programas.
Salvador
@Savior JVM. Existe um contexto pai. Aqui, estamos falando sobre como desligar um aplicativo de inicialização Spring. Você normalmente não fecha os aplicativos da web dessa maneira. Portanto, esse mecanismo é geralmente usado para aplicativos de curta duração que fazem algo e precisam ser interrompidos. Por padrão, o Spring boot continuará rodando mesmo depois que você terminar o processamento em lote, então é aí que você deseja usar este mecanismo.
ACV
3

No aplicativo você pode usar SpringApplication. Isso tem um exit()método estático que leva dois argumentos: oe ApplicationContextum ExitCodeGenerator:

ou seja, você pode declarar este método:

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

Nos testes de integração, você pode alcançá-lo adicionando @DirtiesContextanotações no nível da classe:

  • @DirtiesContext(classMode=ClassMode.AFTER_CLASS) - O ApplicationContext associado será marcado como sujo após a aula de teste.
  • @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - O ApplicationContext associado será marcado como sujo após cada método de teste na classe.

ie

@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 {
...
artesão mágico
fonte
Está bem. Onde devo obter ExecutorServiceExitCodeGenerator? Se for um bean, você pode mostrar o código do snippet de criação (e de qual classe ele foi criado)? Em qual classe o método shutDown (ExecutorServiceExitCodeGenerator exitCodeGenerator) deve ser colocado?
Vlad G.
2

Isso garantirá que o aplicativo SpringBoot seja fechado corretamente e os recursos sejam liberados de volta para o sistema operacional,

@Autowired
private ApplicationContext context;

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

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