Spring Boot啓動後執行特定操做,而後自動中止

前言

咱們原來使用 Spring Boot 通常都是在 web 工程中,執行順序是啓動內嵌 tomcat 容器 → 全部服務待命 → 等待請求的過來 → 處理請求,如此循環,當須要中止的話要在外部執行命令中止。html


問題

可是問題來了,如今有一個特殊的需求,只須要 Spring Boot 執行一次,順序是啓動Spring Boot → 執行 service 的一些方法→中止容器。整個過程不須要人工干預,Spring Boot 啓動後自動執行,執行後自動中止,不像原來那樣掛起等待。java


分析

首先分析一下需求,能夠發現主要有兩個特殊的點:web

  1. 啓動Spring Boot後執行特定的操做;
  2. 執行操做後自動中止;

解決

1. 對於第一個問題,在網上很容易找到答案。只須要添加一個 Listeners 便可:
public class ApplicationStartup implements ApplicationListener<ContextRefreshedEvent> {
    public void onApplicationEvent(ContextRefreshedEvent event)     {
       // 想要執行的操做
    }
}

添加到SpringApplication中:spring

public static void main(String[] args) {
   SpringApplication springApplication = new SpringApplication(Application.class);
   springApplication.addListeners(new ApplicationStartup());
   springApplication.run(args);
}
2. 重點是第二個問題:自動中止 Spring Boot,找遍了搜索引擎都沒有找到方法,應該是這個需求比較奇葩,通常人不會這麼用。但是遇到了問題仍是要解決,因而我就想,什麼狀況下Spring Boot會中止呢?出現異常!是的,啓動Spring Boot的過程當中,只要出現異常,容器就會中止,這是開發過程當中常常出現的問題。那麼在發現異常的時候,確定有某些機制,準確的說是某些代碼使得容器中止。找到了線索,方法天然就有了,我在Spring Boot執行完操做後,就是在 ApplicationStartup 的最後手動的拋出了一個異常,而後開啓 debug 模式,追蹤進去,發如今 EmbeddedWebApplicationContext 類中有一個很是明顯的中止操做:
public final void refresh() throws BeansException, IllegalStateException {
  try {
          super.refresh();
      } catch (RuntimeException var2) {
          // 就是這句!
          this.stopAndReleaseEmbeddedServletContainer();
          throw var2;
      }
}

進到方法,發現了位於 EmbeddedWebApplicationContext 類中的罪魁禍首:tomcat

private void stopAndReleaseEmbeddedServletContainer() {
    EmbeddedServletContainer localContainer = this.embeddedServletContainer;
    if(localContainer != null) {
        try {
                localContainer.stop();
                this.embeddedServletContainer = null;
            } catch (Exception var3) {
                throw new IllegalStateException(var3);
        }
    }
}

就是這句localContainer.stop()至此,咱們知道了Spring Boot中止容器的方法,就是調用EmbeddedServletContainerstop方法。因此咱們只須要獲取到EmbeddedServletContainer就能夠了。app

而要獲取EmbeddedServletContainer,就要首先獲取EmbeddedWebApplicationContext。觀察EmbeddedWebApplicationContext,發現它叫作 Context,那是否實現自ApplicationContext呢?經過引用關係,我發現它確實實現了ApplicationContext,這樣就好辦了,由於在平常開發中,咱們常常要獲取ApplicationContext和獲取bean,已經封裝有一個工具類BeanToolspring-boot

@Component
public class BeanTool extends ApplicationObjectSupport implements ApplicationContextAware {
    static ApplicationContext context;
    private static ApplicationContext applicationContext = null;

    public static void setApplicationContext(WebApplicationContext applicationContext) {
        BeanTool.applicationContext = applicationContext;
    }

    public BeanTool getInstance() {
        return new BeanTool();
    }

    protected void initApplicationContext(ApplicationContext context) {
        super.initApplicationContext(context);
        if (applicationContext == null) {
            applicationContext = context;
        }
    }

    public static ApplicationContext getAppContext() {
        return applicationContext;
    }

    public static Object getBean(String name) {
        return getAppContext().getBean(name);
    }

    public static Object getBean(Class clazz) {
        return getAppContext().getBean(clazz);
    }
}

這個工具類要註冊到Spring Boot的入口類中:工具

/**
 * bean工具類,能夠在普通類中獲取spring建立的bean
 */
@Bean
public BeanTool beanTool() {
    return new BeanTool();
}

如今就差最後一步了!在ApplicationStartup的最後,調用BeanTool獲取EmbeddedWebApplicationContext,從而獲取EmbeddedServletContainer,最後調用其stop方法:this

EmbeddedWebApplicationContext context =(EmbeddedWebApplicationContext)BeanTool.getAppContext();
context.getEmbeddedServletContainer().stop();

執行,能夠看到執行完操做後,容器中止了,大功告成!搜索引擎


最後

從這個的經歷中,能夠看到問題的產生,問題的分析和問題的解決之中的是如何思考的。面對未知的問題,只要採起合適的方法,一步一步試錯,必定能找到解決的方法。

「你我的的項目,應該有四分之一會失敗,不然就說明你的冒險精神不夠。」(Expect and hope that a quarter of your projects fail. If not, you’re not taking enough risks. –Adam Smith)

相關文章
相關標籤/搜索