Encerrar um aplicativo de inicialização Spring
1. Visão geral
Gerenciar o ciclo de vida do Spring Boot Application é muito importante para um sistema pronto para produção. O container Spring lida com a criação, inicialização e destruição de todos os Beans com a ajuda doApplicationContext.
A ênfase deste artigo é a fase de destruição do ciclo de vida. Mais especificamente, veremos diferentes maneiras de desligar um aplicativo Spring Boot.
Para saber mais sobre como configurar um projeto usando Spring Boot, verifique o artigoSpring Boot Starter ou leiaSpring Boot Configuration.
2. Ponto final de desligamento
Por padrão, todos os endpoints são habilitados no Spring Boot Application, exceto/shutdown; isso é, naturalmente, parte dos pontos finais deActuator.
Esta é a dependência do Maven para configurá-los:
org.springframework.boot
spring-boot-starter-actuator
E, se quisermos também configurar o suporte de segurança, precisamos:
org.springframework.boot
spring-boot-starter-security
Por último, habilitamos o endpoint de desligamento no arquivoapplication.properties:
management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true
endpoints.shutdown.enabled=true
Observe que também precisamos expor os pontos de extremidade do atuador que queremos usar. No exemplo acima, expusemos todos os pontos finais do atuador que incluirão o ponto final/shutdown.
To shut down the Spring Boot application, we simply call a POST method like this:
curl -X POST localhost:port/actuator/shutdown
Nesta chamada, oport representa a porta do atuador.
3. Fechar contexto do aplicativo
Também podemos chamar o métodoclose() diretamente usando o contexto do aplicativo.
Vamos começar com um exemplo de criação de um contexto e fechá-lo:
ConfigurableApplicationContext ctx = new
SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run();
System.out.println("Spring Boot application started");
ctx.getBean(TerminateBean.class);
ctx.close();
This destroys all the beans, releases the locks, then closes the bean factory. Para verificar o desligamento do aplicativo, usamos o retorno de chamada do ciclo de vida padrão do Spring com a anotação@PreDestroy:
public class TerminateBean {
@PreDestroy
public void onDestroy() throws Exception {
System.out.println("Spring Container is destroyed!");
}
}
Também temos que adicionar um bean deste tipo:
@Configuration
public class ShutdownConfig {
@Bean
public TerminateBean getTerminateBean() {
return new TerminateBean();
}
}
Aqui está o resultado após a execução deste exemplo:
Spring Boot application started
Closing [email protected]
DefaultLifecycleProcessor - Stopping beans in phase 0
Unregistering JMX-exposed beans on shutdown
Spring Container is destroyed!
O que é importante ter em mente aqui:while closing the application context, the parent context isn’t affected due to separate lifecycles.
3.1. Feche o contexto atual do aplicativo
No exemplo acima, criamos um contexto de aplicativo filho e usamos o métodoclose() para destruí-lo.
Se quisermos fechar o contexto atual, uma solução é simplesmente chamar o endpoint/shutdown do atuador.
No entanto, também podemos criar nosso próprio terminal personalizado:
@RestController
public class ShutdownController implements ApplicationContextAware {
private ApplicationContext context;
@PostMapping("/shutdownContext")
public void shutdownContext() {
((ConfigurableApplicationContext) context).close();
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.context = ctx;
}
}
Aqui, adicionamos um controlador que implementa a interfaceApplicationContextAware e substitui o método setter para obter o contexto do aplicativo atual. Então, em um método de mapeamento, estamos simplesmente chamando o métodoclose().
Podemos então chamar nosso novo terminal para desligar o contexto atual:
curl -X POST localhost:port/shutdownContext
Claro, se você adicionar um endpoint como este em um aplicativo da vida real, você também desejará protegê-lo.
4. SairSpringApplication
SpringApplication registra um ganchoshutdown com a JVM para garantir que o aplicativo seja encerrado de forma adequada.
Beans podem implementar a interfaceExitCodeGenerator para retornar um código de erro específico:
ConfigurableApplicationContext ctx = new SpringApplicationBuilder(Application.class)
.web(WebApplicationType.NONE).run();
int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
@Override
public int getExitCode() {
// return the error code
return 0;
}
});
System.exit(exitCode);
O mesmo código com o aplicativo do Java 8 lambdas:
SpringApplication.exit(ctx, () -> 0);
After calling the System.exit(exitCode), the program terminates with a 0 return code:
Process finished with exit code 0
5. Mate o processo do aplicativo
Por fim, também podemos desligar um aplicativo de inicialização Spring de fora do aplicativo usando um script bash. Nossa primeira etapa para esta opção é fazer com que o contexto do aplicativo grave seu PID em um arquivo:
SpringApplicationBuilder app = new SpringApplicationBuilder(Application.class)
.web(WebApplicationType.NONE);
app.build().addListeners(new ApplicationPidFileWriter("./bin/shutdown.pid"));
app.run();
A seguir, crie um arquivoshutdown.bat com o seguinte conteúdo:
kill $(cat ./bin/shutdown.pid)
A execução deshutdown.bat extrai a ID do processo do arquivoshutdown.pide usa o comandokill para encerrar o aplicativo de inicialização.
6. Conclusão
Neste artigo rápido, cobrimos alguns métodos simples que podem ser usados para desligar um aplicativo Spring Boot em execução.
Embora seja responsabilidade do desenvolvedor escolher um método apropriado; todos esses métodos devem ser usados por design e propositalmente.
Por exemplo,.exit() é preferível quando precisamos passar um código de erro para outro ambiente, digamos JVM para outras ações. UsandoApplication PID gives more flexibility, as we can also start or restart the application com o uso do script bash.
Finalmente,/shutdown está aqui para tornar possívelterminate the applications externally via HTTP. Para todos os outros casos,.close() funcionará perfeitamente.
Como de costume, o código completo para este artigo está disponível emGitHub project.