本文我们开始分析SpringBoot关闭的时候都做了哪些事。核心流程梳理如下:
- 从LiveBeansView移除掉维护的上下文
- 广播ContextClosedEvent事件
- 触发生命周期处理器的onClose方法,这里会stopBean,也就是触发那些Lifecycle实例的stop方法
- 销毁BeanFactory中的所有DisposableBean并清空一些缓存
- 关闭BeanFactory
- 停止服务,这里会stop Tomcat
- 将earlyApplicationListeners 数据拷贝给applicationListeners
- 设置active状态为false
如下所示触发ConfigurableApplicationContext的close方法时就会触发服务的关闭。
public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(RecommendApplication.class, args); applicationContext.close(); }
AbstractApplicationContext的close方法
如下所示,其会将具体动作委派给doClose方法,然后尝试移除掉JVM shutdown hook。
@Override public void close() { synchronized (this.startupShutdownMonitor) { doClose(); // If we registered a JVM shutdown hook, we don't need it anymore now: // We've already explicitly closed the context. if (this.shutdownHook != null) { try { // 移除钩子 Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException ex) { // ignore - VM is already shutting down } } } }
【1】核心关闭方法
AbstractApplicationContext的doClose
protected void doClose() { // Check whether an actual close attempt is necessary... // 如果当前应用是激活状态且没有关闭,才执行if方法体 if (this.active.get() && this.closed.compareAndSet(false, true)) { if (logger.isDebugEnabled()) { logger.debug("Closing " + this); } // 从LiveBeansView移除掉维护的上下文 LiveBeansView.unregisterApplicationContext(this); try { // Publish shutdown event. // 广播ContextClosedEvent事件 publishEvent(new ContextClosedEvent(this)); } catch (Throwable ex) { logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex); } // Stop all Lifecycle beans, to avoid delays during individual destruction. if (this.lifecycleProcessor != null) { try { // 触发生命周期处理器的onClose方法,这里会stopBean,也就是触发那些Lifecycle实例的stop方法 this.lifecycleProcessor.onClose(); } catch (Throwable ex) { logger.warn("Exception thrown from LifecycleProcessor on context close", ex); } } // Destroy all cached singletons in the context's BeanFactory. // 销毁BeanFactory中的所有DisposableBean并清空一些缓存 destroyBeans(); // Close the state of this context itself. //关闭BeanFactory closeBeanFactory(); // Let subclasses do some final clean-up if they wish... //停止服务,这里会stop Tomcat onClose(); // Reset local application listeners to pre-refresh state. if (this.earlyApplicationListeners != null) { this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); } // Switch to inactive. // 设置active状态为false this.active.set(false); } }
① unregisterApplicationContext
关于LiveBeansView.unregisterApplicationContext(this),如下所示在SpringBoot启动流程中AbstractApplicationContext的finishRefresh方法中会将应用上下文注册到LiveBeansView中,在关闭过程中就会触发unregisterApplicationContext移除应用上下文。具体来讲就是
- 从LiveBeansView的成员applicationContexts移除应用上下文
- 触发MBeanServer的unregisterMBean方法
- 将LiveBeansView的成员applicationName置为null
② publishEvent
这里会发布ContextClosedEvent事件,有如下监听器对其感兴趣:
0 = {RestartApplicationListener@10934} 1 = {LoggingApplicationListener@10935} 2 = {DelegatingApplicationListener@2936} 3 = {DelegatingApplicationListener@10936}
LoggingApplicationListener会触发this.loggingSystem.cleanUp();。本文这里并没有配置context.listener.classes属性,所以其他监听器对该事件并无作用。
③ lifecycleProcessor.onClose()
触发生命周期处理器的关闭方法,默认实例是DefaultLifecycleProcessor,其方法如下所示:
@Override public void onClose() { //触发Lifecycle类型的bean的stop方法 stopBeans(); //修改运行状态为false this.running = false; }
stopBeans方法如下所示:
private void stopBeans() { //获取生命周期bean,本文这里只有一个documentationPluginsBootstrapper // key = "documentationPluginsBootstrapper" --DocumentationPluginsBootstrapper Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans(); Map<Integer, LifecycleGroup> phases = new HashMap<>(); lifecycleBeans.forEach((beanName, bean) -> { int shutdownPhase = getPhase(bean); LifecycleGroup group = phases.get(shutdownPhase); if (group == null) { group = new LifecycleGroup(shutdownPhase, this.timeoutPerShutdownPhase, lifecycleBeans, false); phases.put(shutdownPhase, group); } group.add(beanName, bean); }); if (!phases.isEmpty()) { List<Integer> keys = new ArrayList<>(phases.keySet()); keys.sort(Collections.reverseOrder()); for (Integer key : keys) { // 触发LifecycleGroup的stop,最终会触发生命周期bean的stop方法 phases.get(key).stop(); } } }
如上所示首先获取所有的Lifecycle实例,然后安装shutdownPhase 进行分组,按组进行比遍历触发bean实例的stop方法。这里需要说明的时,如果bean实例有依赖bean(dependentBeans),那么会先触发这些依赖bean的销毁。
【2】 destroyBeans
AbstractApplicationContext的destroyBeans方法最终会触发DefaultListableBeanFactory的destroySingletons方法。
protected void destroyBeans() { getBeanFactory().destroySingletons(); }
DefaultListableBeanFactory的destroySingletons方法如下所示:
// DefaultListableBeanFactory @Override public void destroySingletons() { //触发父类DefaultSingletonBeanRegistry的销毁方法 super.destroySingletons(); //更新Set<String> manualSingletonNames updateManualSingletonNames(Set::clear, set -> !set.isEmpty()); //这里会清空两个ConcurrentHashMap: //allBeanNamesByType和singletonBeanNamesByType clearByTypeCache(); }
① DefaultSingletonBeanRegistry的destroySingletons
父类DefaultSingletonBeanRegistry的destroySingletons方法如下所示,首先遍历每一个disposableBean进行销毁,然后清空一些缓存。
// DefaultSingletonBeanRegistry public void destroySingletons() { if (logger.isTraceEnabled()) { logger.trace("Destroying singletons in " + this); } // 对一级缓存加锁,修改单例正在销毁标识为true synchronized (this.singletonObjects) { this.singletonsCurrentlyInDestruction = true; } String[] disposableBeanNames; synchronized (this.disposableBeans) { disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); } // 销毁每一个实现了disposableBean接口的单例bean for (int i = disposableBeanNames.length - 1; i >= 0; i--) { destroySingleton(disposableBeanNames[i]); } // 清空三个ConcurrentHashMap // 本文这里为空 this.containedBeanMap.clear(); // 存储的是 bean---被哪些bean所需要 this.dependentBeanMap.clear(); //存储的是 this.dependenciesForBeanMap.clear(); //清空单例缓存 clearSingletonCache(); }
如上方法所示,其获取disposableBeanNames 遍历循环触发每一个bean的销毁流程,然后清空三个ConcurrentHashMap,最后清空单例缓存。
销毁单个bean
我们先看一下disposableBean的销毁,这里首先触发的DefaultListableBeanFactory的destroySingleton方法。
// DefaultListableBeanFactory @Override public void destroySingleton(String beanName) { // 触发父类DefaultSingletonBeanRegistry的销毁方法 super.destroySingleton(beanName); // 从manualSingletonNames这个Set中移除beanName removeManualSingletonName(beanName); //清理缓存--又看到了,是不是多余? clearByTypeCache(); }
父类DefaultSingletonBeanRegistry的destroySingleton方法
也就是说DisposableBean 的销毁方法其实是被父类DefaultSingletonBeanRegistry来执行的。
public void destroySingleton(String beanName) { // 从singletonObjects、singletonFactories、earlySingletonObjects及registeredSingletons移除 removeSingleton(beanName); // Destroy the corresponding DisposableBean instance. DisposableBean disposableBean; //从Map<String, Object> disposableBeans移除 synchronized (this.disposableBeans) { disposableBean = (DisposableBean) this.disposableBeans.remove(beanName); } //销毁bean destroyBean(beanName, disposableBean); }
关于destroyBean(beanName, disposableBean);我们本文不做进一步分析,详情参考SpringBoot关闭过程中是如何销毁一个DisposableBean的?。
清空单例缓存
clearSingletonCache方法如下所示,清空了一级、三级、二级缓存以及Set<String> registeredSingletons。
protected void clearSingletonCache() { synchronized (this.singletonObjects) { // 清空一级缓存 this.singletonObjects.clear(); //清空三级缓存 this.singletonFactories.clear(); //清空二级缓存 this.earlySingletonObjects.clear(); //清空单例bean Set this.registeredSingletons.clear(); this.singletonsCurrentlyInDestruction = false; } }
② updateManualSingletonNames
这里应用了jdk1.8的新特性,主要目的就是如果manualSingletonNames不为空,那么就clear见清空。
updateManualSingletonNames(Set::clear, set -> !set.isEmpty()); //DefaultListableBeanFactory private void updateManualSingletonNames(Consumer<Set<String>> action, Predicate<Set<String>> condition) { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { if (condition.test(this.manualSingletonNames)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); action.accept(updatedSingletons); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase if (condition.test(this.manualSingletonNames)) { action.accept(this.manualSingletonNames); } } }
③ clearByTypeCache
这里清理的是allBeanNamesByType和singletonBeanNamesByType。
private void clearByTypeCache() { this.allBeanNamesByType.clear(); // 本文这里为空 this.singletonBeanNamesByType.clear(); } /** Map of singleton and non-singleton bean names, keyed by dependency type. */ private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64); /** Map of singleton-only bean names, keyed by dependency type. */ private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64);
【3】其余方法
① closeBeanFactory
AbstractRefreshableApplicationContext的closeBeanFactory方法如下所示,设置当前应用上下文维护的beanFactory的SerializationId为null,然后beanFactory指向null。
@Override protected final void closeBeanFactory() { synchronized (this.beanFactoryMonitor) { if (this.beanFactory != null) { this.beanFactory.setSerializationId(null); this.beanFactory = null; } } }
② onClose
这里会触发ServletWebServerApplicationContext的onClose方法,这里会stop tomcat。
@Override protected void onClose() { // super是AbstractApplicationContext,其是一个空方法 super.onClose(); //触发webServer的stop方法 stopAndReleaseWebServer(); }
ServletWebServerApplicationContext的stopAndReleaseWebServer方法
private void stopAndReleaseWebServer() { WebServer webServer = this.webServer; if (webServer != null) { try { // 触发stop方法 webServer.stop(); // 引用指向null this.webServer = null; } catch (Exception ex) { throw new IllegalStateException(ex); } } }
继续往下看,这里是TomcatWebServer的stop方法。
@Override public void stop() throws WebServerException { synchronized (this.monitor) { boolean wasStarted = this.started; try { // 修改启动标志为false this.started = false; try { // 停止tomcat stopTomcat(); // 销毁tomcat实例 this.tomcat.destroy(); } catch (LifecycleException ex) { // swallow and continue } } catch (Exception ex) { throw new WebServerException("Unable to stop embedded Tomcat", ex); } finally { if (wasStarted) { containerCounter.decrementAndGet(); } } } }
stopTomcat方法会触发tomcat实例的stop方法进而触发server的stop方法,tomcat实例的destroy方法也会触发server的destroy方法。
public void stop() throws LifecycleException { getServer(); server.stop(); } public void destroy() throws LifecycleException { getServer(); server.destroy(); // Could null out objects here }
③ doClose的末尾方法
如下所示,这里会将earlyApplicationListeners 的数据复制到applicationListeners中,然后将active状态设置为false。
if (this.earlyApplicationListeners != null) { this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); } // Switch to inactive. this.active.set(false);
④ 移除钩子
当doClose方法执行完后,如下所示最终会移除JVM shutdown hook。
ShutdownHooks的remove方法如下所示,会移除当前应用上下文的销毁钩子,这里移除的是Thread[SpringContextShutdownHook,5,main]。
static synchronized boolean remove(Thread hook) { if(hooks == null) throw new IllegalStateException("Shutdown in progress"); if (hook == null) throw new NullPointerException(); return hooks.remove(hook) != null; }





