SpringBoot关闭时都做了哪些事?

本文我们开始分析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启动流程中AbstractApplicationContextfinishRefresh方法中会将应用上下文注册到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

AbstractApplicationContextdestroyBeans方法最终会触发DefaultListableBeanFactorydestroySingletons方法。

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的销毁,这里首先触发的DefaultListableBeanFactorydestroySingleton方法。

// 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

AbstractRefreshableApplicationContextcloseBeanFactory方法如下所示,设置当前应用上下文维护的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;
  }

在这里插入图片描述

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值