因業務需求,從内部停止并關閉springboot項目
首先說下遇到的需求,在項目中甯願不執行也不能執行錯誤的需求時有發生,最近在做一個平台,在儲存收款賬戶時考慮安全問題,在啟動項目時作為必須校驗項進行,如果校驗不通過,阻止項目允許。
在百度上各種查,沒有此類操作,現在研究出來了發現超級簡單。寫這篇文章的目的主要是記錄下研究過程。心急的同學可以直接看的一部分,最終結果。
1、終于搞出來了
spring 的context(實作了ConfigurableApplicationContext的context)有一個close方法,在需要的地方執行context.close就好了。
2、我是怎麼搞出來的
- 一、内事不決問百度(主要是Google不了啊~~)
剛開始我像往常一樣,期待網友們有一個成熟的解決方案。但是,經過幾個小時的查找和篩選,沒有找到我想要的。也不是一無所獲。網友們都很樂于奉獻,隻要搜springboot 啟動,關閉,終止的,都能查到兩種方法。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBHL0FWby9mZvwVZnFWbp1zczV2YvJHctM3cv1Ce-kXVU9EMFpmTxEEVNRTQU1EejRVT3lkeMBjVtJWd0ckW65UbM5WOHJWa1knW0xmMMZ3bENGMShUYvwlbj5yZtlmbkN3YuQnclZnbvN2Ztl2Lc9CX6MHc0RHaiojIsJye.jpg)
對于這個結果剛開始我是很不開森的,因為隻有這個結果,而且能搜到的都是這個結果,達到了一遇到這個結果就關頁面的程度,最後實在找不到解決方案了。我憤怒了,既然沒有人有研究,我就來貢獻時間。
- 二、Java程式員要想提升還是得看源碼跟流程
既然spring官方提供了兩種關閉服務的方法,說明可以做到,既然他們能做到,我也能!于是按照網友提供的步驟,下面引用網友提供的 通過http發送shutdown的方法 來研究
1.在pom.xml中引入actuator依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.在application.properties檔案開啟shutdown endpoint,SpringBoot的endpoints.shutdown.enabled預設是關閉的。
#啟用shutdown
endpoints.shutdown.enabled=true
#禁用密碼驗證
endpoints.shutdown.sensitive=false
經過簡單的兩步就能實作遠端調用關閉系統,但這不是我想要的,我要找的是在系統裡直接調用某個方法就能實作自動關閉。
要查找這個方法必須從官方提供的遠端調用入手,遠端調用使用的HTTP請求,于是要查找HTTP請求的服務接口,沒辦法隻能列印日志慢慢找
找到這個入口後開始一步步debug,很痛苦的,因為你不知道哪一步系統就關閉了,隻能在關閉系統的方法上加斷點再次啟動系統,執行關閉,如此反複查找了好幾次,終于讓我發現了關閉服務的老巢,哈哈哈哈!!!
老巢,spring-boot-actuator-1.5.4RELEASE.jar包裡的ShutdownEndpoint這個類,下面貼出來主要代碼,這段代碼主要是說能關閉系統時,傳回說正在關閉,啟動關閉線程,調用this.context.close()。我去,終于讓我知道了你的秘密,使用context就能關閉。好像我spring有擷取context的方法吧,我去查查(本人技術水準有限,隻是知道有這個事兒,這是後面故事的開始)。
ShutdownEndpoint的主要代碼
public ShutdownEndpoint() {
super("shutdown", true, false);
}
@Override
public Map<String, Object> invoke() {
if (this.context == null) {
return NO_CONTEXT_MESSAGE;
}
try {
return SHUTDOWN_MESSAGE;
}
finally {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500L);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
ShutdownEndpoint.this.context.close();
}
});
thread.setContextClassLoader(getClass().getClassLoader());
thread.start();
}
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
if (context instanceof ConfigurableApplicationContext) {
this.context = (ConfigurableApplicationContext) context;
}
}
spring有擷取context的方法
中國網友就是給力,一搜一大把,都是一樣的 —— 實作ApplicationContextAware接口
嗯,照着做,實作接口,東複制下,西複制下,懷着無比期待的心情執行測試……咦怎麼沒有關閉!!!!!
我去,debug,然後各種查,痛苦死了,下面給出錯誤代碼(哪裡錯了自己看),希望網友們引以為戒
public class ShutdownContext implements ApplicationContextAware
{
private ConfigurableApplicationContext context;
public void showdown(){
if (null != context){
context.close();
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException{
if (context instanceof ConfigurableApplicationContext) {
this.context = (ConfigurableApplicationContext) applicationContext;
}
}
}
————————————————美麗的分割線—————————————————————
有網友說使用我的代碼運作不了,時隔好久也不記得哪裡錯了,對比了下代碼才發現最後一個判斷錯誤了,應該判斷傳入參數,判斷了成員變量。下面是完整代碼
import org.springframework.beans.BeansException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix ="spring")
public class ShutdownContext implements ApplicationContextAware
{
private ConfigurableApplicationContext context;
public void showdown()
{
if (null != context)
{
context.close();
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
if (applicationContext instanceof ConfigurableApplicationContext) {
this.context = (ConfigurableApplicationContext) applicationContext;
}
}
}