1. 概述
管理 Spring Boot 應用的生命週期對於構建生產級別的系統至關重要。 Spring 容器通過 ApplicationContext 來處理所有 Bean 的創建、初始化和銷燬。
本教程的重點是生命週期的銷燬階段。 尤其是,我們將探討不同方法來關閉 Spring Boot 應用。
要了解如何使用 Spring Boot 設置項目,請查看 Spring Boot Starter 文章,或查看 Spring Boot 配置。
2. 關閉端點
默認情況下,所有端點在 Spring Boot 應用程序中都已啓用,除了 /shutdown 端點,它屬於 Actuator 端點的一部分。
以下是設置這些端點的 Maven 依賴項:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>如果我們也希望設置安全支持,我們需要:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>最後,我們將啓用 shutdown 端點,在 application.properties文件中:
management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true
endpoints.shutdown.enabled=true請注意,我們也必須暴露任何要使用的 actuator 端點。 在上面的示例中,我們暴露了所有 actuator 端點,包括 /shutdown 端點。
要關閉 Spring Boot 應用程序,我們將簡單地調用像這樣 POST 方法:
curl -X POST localhost:port/actuator/shutdown在此調用中,port 表示執行端口。
3. 關閉應用程序上下文
我們還可以直接使用應用程序上下文調用 close() 方法。
下面是一個創建上下文並關閉它的示例:
ConfigurableApplicationContext ctx = new
SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run();
System.out.println("Spring Boot application started");
ctx.getBean(TerminateBean.class);
ctx.close();這將銷燬所有 Bean,釋放鎖,然後關閉 Bean 容器. 為了驗證應用程序的關閉,我們將使用 Spring 的標準生命週期回調,帶有 @PreDestroy 註解:
public class TerminateBean {
@PreDestroy
public void onDestroy() throws Exception {
System.out.println("Spring Container is destroyed!");
}
}我們還需要添加該類型的 Bean:
@Configuration
public class ShutdownConfig {
@Bean
public TerminateBean getTerminateBean() {
return new TerminateBean();
}
}這是運行此示例後的輸出:
Spring Boot application started
Closing AnnotationConfigApplicationContext@39b43d60
DefaultLifecycleProcessor - Stopping beans in phase 0
Unregistering JMX-exposed beans on shutdown
Spring Container is destroyed!需要注意的是,在關閉應用程序上下文時,由於它們具有獨立的生命週期,父上下文不會受到影響。
3.1. 關閉當前應用程序上下文
在上面的示例中,我們創建了一個子應用程序上下文,然後使用 close() 方法來銷燬它。
如果想要關閉當前上下文,一種解決方案是簡單地調用 actuator 的 /shutdown 端點。
但是,我們也可以創建自己的自定義端點:
@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;
}
}在這裏,我們添加了一個實現了 ApplicationContextAware 接口的控制器,並覆蓋了 setter 方法以獲取當前應用程序上下文。然後,在映射方法中,我們只需調用 close() 方法。
我們可以然後調用我們的新端點來關閉當前上下文:
curl -X POST localhost:port/shutdownContext當然,如果在實際應用中添加這樣一個端點,我們也需要對其進行安全保護。
4. 退出 SpringApplication
SpringApplication 會向 JVM 註冊一個 shutdown hook,以確保應用程序的正確退出。
Bean 可以實現 ExitCodeGenerator 接口,以返回特定的錯誤代碼:
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);以下是使用 Java 8 람다表達式相同的代碼:
SpringApplication.exit(ctx, () -> 0);在調用 System.exit(exitCode) 後,程序將以 0 返回碼終止
Process finished with exit code 05. 終止應用程序進程
最後,我們還可以通過使用 Bash 腳本從應用程序外部終止 Spring Boot 應用程序。 針對此選項的第一步是讓應用程序上下文將其 PID 寫入文件:
SpringApplicationBuilder app = new SpringApplicationBuilder(Application.class)
.web(WebApplicationType.NONE);
app.build().addListeners(new ApplicationPidFileWriter("./bin/shutdown.pid"));
app.run();接下來,我們將創建一個名為 shutdown.bat 的文件,內容如下:
kill $(cat ./bin/shutdown.pid)執行 shutdown.bat 腳本會從 shutdown.pid 文件中提取進程 ID,並使用 kill 命令終止 Boot 應用程序。
6. 結論
在本文中,我們介紹了幾種可以用來關閉正在運行的 Spring Boot 應用的簡單方法。
雖然開發者需要選擇合適的方法,但所有這些方法都應該有明確的設計和目的。
例如,使用 .exit() 時,當我們需要將錯誤碼傳遞給另一個環境,例如 JVM,以便進行進一步的操作。
使用 Application PID 則提供了更大的靈活性,因為我們可以使用 bash 腳本來啓動或重啓應用程序。
最後,/shutdown 則可以實現通過 HTTP 外部終止應用程序。
對於所有其他情況,.close() 將完美工作。