本文我们开始分析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);
我们本文不做进一步分析清空单例缓存
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;
}