咱們原來使用 Spring Boot 通常都是在 web 工程中,執行順序是啓動內嵌 tomcat 容器 → 全部服務待命 → 等待請求的過來 → 處理請求,如此循環,當須要中止的話要在外部執行命令中止。html
可是問題來了,如今有一個特殊的需求,只須要 Spring Boot 執行一次,順序是啓動Spring Boot → 執行 service 的一些方法→中止容器。整個過程不須要人工干預,Spring Boot 啓動後自動執行,執行後自動中止,不像原來那樣掛起等待。java
首先分析一下需求,能夠發現主要有兩個特殊的點:web
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); }
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中止容器的方法,就是調用EmbeddedServletContainer
的stop
方法。因此咱們只須要獲取到EmbeddedServletContainer
就能夠了。app
而要獲取EmbeddedServletContainer
,就要首先獲取EmbeddedWebApplicationContext
。觀察EmbeddedWebApplicationContext
,發現它叫作 Context
,那是否實現自ApplicationContext
呢?經過引用關係,我發現它確實實現了ApplicationContext
,這樣就好辦了,由於在平常開發中,咱們常常要獲取ApplicationContext
和獲取bean,已經封裝有一個工具類BeanTool
:spring-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)