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);

1
Bom ponto! Chamar close () nisso deve fazer o trabalho.
— Axel Fontaine


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

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

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.


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 {
...

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);
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.