bean 完整生命周期原理的最后一部分,我们来研究应用停止时,bean 的销毁阶段都有哪些动作和过程。
本章主要会涉及到的原理部分:
在测试代码中,我们有主动调用 ApplicationContext
的 stop
和 close
方法,这样 IOC 容器的关闭和销毁流程就会依次执行了。
复制 ctx . stop ();
System . out . println ( "================IOC容器停止成功==================" );
ctx . close ();
}
先来看 stop
的内容。
1. ApplicationContext#stop
复制 public void stop() {
getLifecycleProcessor() . stop ();
publishEvent( new ContextStoppedEvent( this )) ;
}
呦,这个套路不正好跟上一章看到的 start
动作完全相反吗?这个 LifecycleProcessor
中的 stop
方法一定也是先给所有的 Lifecycle
bean 分组,排序,依次调用 stop
方法。当然,我们的推测是绝对正确的,小伙伴们可以跟着源码进去看一看,小册就不贴出来了。
2. ApplicationContext#close
复制 public void close() {
synchronized ( this . startupShutdownMonitor ) {
doClose() ;
if ( this . shutdownHook != null ) {
try {
Runtime . getRuntime () . removeShutdownHook ( this . shutdownHook );
}
// catch ......
}
}
}
关闭容器的动作,仅仅只有一个 doClose
方法,那我们就往里进就好。不过这个 doClose
方法有点长,我们拆解开来讲解。
2.1 广播事件
复制 protected void doClose() {
// Check whether an actual close attempt is necessary...
if ( this . active . get () && this . closed . compareAndSet ( false , true )) {
// logger ......
LiveBeansView . unregisterApplicationContext ( this );
try {
// Publish shutdown event.
publishEvent( new ContextClosedEvent( this )) ;
} // catch ......
// ......
这一段非常简单,广播 ContextClosedEvent
事件而已。前面在事件章节的讲解中我们也说过,SpringFramework 中一共内置了 4 个可供监听的预设事件,它们都是给开发者自己使用 的,SpringFramework 内部是没有任何利用的。
2.2 LifecycleProcessor#onClose
复制 // ......
// Stop all Lifecycle beans, to avoid delays during individual destruction.
if ( this . lifecycleProcessor != null ) {
try {
this . lifecycleProcessor . onClose ();
} // catch ......
}
// ......
等一下?这怎么又是 LifecycleProcessor
?该不会。。。它的 onClose
方法又是跟 stop
一样?看一眼源码:
复制 public void onClose() {
stopBeans() ;
this . running = false ;
}
wtf ???好吧,果然跟上面是一样一样的,那小册就不赘述啦。
2.3 destroyBeans - 销毁bean
复制 // ......
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans() ;
// ......
这一部分我们只看这一个方法,不要觉得它简单哦,它的复杂度还是挺高的,我们进入来看。
复制 protected void destroyBeans() {
getBeanFactory() . destroySingletons ();
}
实现倒也简单,仅仅是获取到 ApplicationContext
内部的 BeanFactory
,让它去销毁所有的单实例 bean 。
2.3.1 DefaultListableBeanFactory#destroySingletons
继续往里走,进入到 DefaultListableBeanFactory
中:
复制 public void destroySingletons() {
super . destroySingletons ();
// 清空单实例bean的名称
updateManualSingletonNames(Set :: clear , set -> ! set . isEmpty()) ;
clearByTypeCache() ;
}
private final Map < Class < ? > , String []> allBeanNamesByType = new ConcurrentHashMap <>( 64 );
private final Map < Class < ? > , String []> singletonBeanNamesByType = new ConcurrentHashMap <>( 64 );
private void clearByTypeCache() {
this . allBeanNamesByType . clear ();
this . singletonBeanNamesByType . clear ();
}
这段代码中首先会调用父类 DefaultSingletonBeanRegistry
的 destroySingletons
方法,随后会清空所有的单实例 bean 的名称,以及 “类型到 name ” 的映射。( allBeanNamesByType
中保存了 bean 的类型包含的所有 bean ,如 Person
类型的 bean 在 IOC 容器中包含 master
和 admin
)
2.3.2 DefaultSingletonBeanRegistry#destroySingletons
重点还是在 DefaultSingletonBeanRegistry
中,进入它的 destroySingletons
方法实现:
复制 public void destroySingletons() {
// logger ......
synchronized ( this . singletonObjects ) {
this . singletonsCurrentlyInDestruction = true ;
}
String [] disposableBeanNames;
synchronized ( this . disposableBeans ) {
// set -> array
disposableBeanNames = StringUtils . toStringArray ( this . disposableBeans . keySet ());
}
// 销毁所有单实例bean
for ( int i = disposableBeanNames . length - 1 ; i >= 0 ; i -- ) {
destroySingleton(disposableBeanNames[i]) ;
}
// 清空一切缓存
this . containedBeanMap . clear ();
this . dependentBeanMap . clear ();
this . dependenciesForBeanMap . clear ();
clearSingletonCache() ;
}
这段源码看着挺长,核心就一句:重载的 destroySingleton(beanName)
。下面的好多 Map
的 clear
动作,以及 clearSingletonCache
方法,都是清除掉缓存而已,核心还是逐个销毁单实例 bean 。
2.3.3 destroySingleton
进入到单个 bean 的销毁流程,Debug 发现又回到 DefaultListableBeanFactory
了:
复制 public void destroySingleton( String beanName) {
super . destroySingleton (beanName);
removeManualSingletonName(beanName) ;
clearByTypeCache() ;
}
-.- 又来这一套是吧,合着销毁一个,跟销毁一堆的套路是一样一样的,那我们继续往里进,还是走到 DefaultSingletonBeanRegistry
中:
复制 public void destroySingleton( String beanName) {
// Remove a registered singleton of the given name, if any.
// 此处会清理掉BeanFactory中设计的用于处理bean循环依赖的三级缓存
// 如果小伙伴对此感兴趣,可以参考boot小册第15章
removeSingleton(beanName) ;
// Destroy the corresponding DisposableBean instance.
DisposableBean disposableBean;
synchronized ( this . disposableBeans ) {
disposableBean = (DisposableBean) this . disposableBeans . remove (beanName);
}
destroyBean(beanName , disposableBean) ;
}
销毁单个 bean 的动作总共就两步:清空 **BeanFactory**
中用于处理循环依赖的缓存,回调 bean 的销毁动作 。重要的方法仍然在最后,我们继续往里深入:
2.3.4 destroyBean
这个方法又是好长啊,我们还是拆解开来看。
销毁依赖的bean
复制 protected void destroyBean( String beanName , @ Nullable DisposableBean bean) {
Set < String > dependencies;
synchronized ( this . dependentBeanMap ) {
dependencies = this . dependentBeanMap . remove (beanName);
}
if (dependencies != null ) {
// logger ......
for ( String dependentBeanName : dependencies) {
destroySingleton(dependentBeanName) ;
}
}
// ......
}
首先,在销毁一个 bean 时,如果它有依赖其它的 bean ,则首要目标不是销毁自己,而是先销毁那些依赖的 bean ,所以这里会有递归调用上面 destroySingleton
方法的动作。
自定义bean销毁方法的回调
复制 // ......
// Actually destroy the bean now...
if (bean != null ) {
try {
bean . destroy ();
}
// catch ......
}
// ......
动作很简单,一个 destroy
方法就完事了,点击去看一下吧:
复制 public void destroy() {
if ( ! CollectionUtils . isEmpty ( this . beanPostProcessors )) {
// 回调DestructionAwareBeanPostProcessor
// 此处会有执行@PreDestroy注解标注的销毁方法
for ( DestructionAwareBeanPostProcessor processor : this . beanPostProcessors ) {
processor . postProcessBeforeDestruction ( this . bean , this . beanName );
}
}
if ( this . invokeDisposableBean ) {
// logger ......
try {
// ......
else {
// 回调DisposableBean接口的destroy方法
((DisposableBean) this . bean ) . destroy ();
}
}
// catch ......
}
// 回调自定义的destroy-method方法
if ( this . destroyMethod != null ) {
invokeCustomDestroyMethod( this . destroyMethod ) ;
}
else if ( this . destroyMethodName != null ) {
Method methodToInvoke = determineDestroyMethod( this . destroyMethodName ) ;
if (methodToInvoke != null ) {
invokeCustomDestroyMethod( ClassUtils . getInterfaceMethodIfPossible(methodToInvoke)) ;
}
}
}
到这里我们就发现了,它会把我们常用的三种 bean 的销毁手段都依次执行一遍,同时也就解释了 bean 的销毁阶段生命周期回调的顺序:**@PreDestroy**
→ **DisposableBean**
→ **destroy-method**
。
处理bean中bean
复制 // ......
// Trigger destruction of contained beans...
Set < String > containedBeans;
synchronized ( this . containedBeanMap ) {
// Within full synchronization in order to guarantee a disconnected Set
containedBeans = this . containedBeanMap . remove (beanName);
}
if (containedBeans != null ) {
for ( String containedBeanName : containedBeans) {
destroySingleton(containedBeanName) ;
}
}
// ......
这个概念不是很好理解吧,bean 中 bean 是什么鬼?还记得第 9 章中,我们在写复杂类型注入的时候,说可以在 property 中再写 <bean>
这回事吧,不记得也没有关系,我改一下原有的测试代码,想必你一下子就能想起来了吧:
复制 < bean id = "cat" class = "com.linkedbear.spring.lifecycle.e_source.bean.Cat" >
< property name = "name" value = "mimi" />
< property name = "master" >
< bean class = "com.linkedbear.spring.lifecycle.e_source.bean.Person" />
</ property >
</ bean >
哎,这种嵌套 bean ,就会记录在 DefaultSingletonBeanRegistry
的 containedBeanMap
中,既然外头的 bean 要销毁了,那里头的这些 bean 也就应该被销毁了,所以这里又是取到那些内部构造好的 bean ,依次递归调用 destroySingleton
方法来销毁这些 bean 。
销毁被依赖的bean
复制 // ......
// Remove destroyed bean from other beans' dependencies.
synchronized ( this . dependentBeanMap ) {
for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {
Map . Entry < String , Set < String >> entry = it . next ();
Set < String > dependenciesToClean = entry . getValue ();
dependenciesToClean . remove (beanName);
if ( dependenciesToClean . isEmpty ()) {
it . remove ();
}
}
}
// Remove destroyed bean's prepared dependency information.
this . dependenciesForBeanMap . remove (beanName);
}
注意这里的说法,这里是销毁那些被 依赖的 bean ,由于一个 bean 可能被其它 bean 引用或者依赖,所以在最后一步,当自己销毁之后,那些依赖了当前 bean 的 bean 也就应该被销毁 。套路都是一样的,取到那些被依赖的 bean 的名称,依次递归调用 destroySingleton
方法销毁。
到这里,destroyBeans
销毁单实例 bean 的逻辑就全部走完了,继续往下看 doClose
方法还干了什么事。
2.4 关闭BeanFactory
复制 // Close the state of this context itself.
closeBeanFactory() ;
这个步骤,说是关闭 BeanFactory
,实际上更接近于 “销毁” ,因为原来的 BeanFactory
无论如何都无法继续用了。
在基于 xml 和基于注解驱动的两种 ApplicationContext
的实现里,它们的策略既相似又不同:
复制 // AbstractRefreshableApplicationContext
protected final void closeBeanFactory() {
DefaultListableBeanFactory beanFactory = this . beanFactory ;
if (beanFactory != null ) {
beanFactory . setSerializationId ( null );
this . beanFactory = null ;
}
}
在基于 xml 配置文件的 ApplicationContext
中,它会获取到原有的 BeanFactory
,移除序列化 ID ,并直接丢弃原来的 BeanFactory
。
复制 // GenericApplicationContext
protected final void closeBeanFactory() {
this . beanFactory . setSerializationId ( null );
}
在基于注解驱动的 ApplicationContext
中,它只会给内部组合的 BeanFactory
移除序列化 ID 而已。
2.4.1 GenericApplicationContext不允许被重复刷新的原因
这个时候可能会有小伙伴们产生疑惑:之前不是说 GenericApplicationContext
不允许重复刷新吗?既然它只是移除了一下序列化 ID ,那刷新的时候重新设置一个不就行了吗?哎,这个时候我们要了解 GenericApplicationContext
中的另外一个成员了:
复制 private final AtomicBoolean refreshed = new AtomicBoolean() ;
protected final void refreshBeanFactory() throws IllegalStateException {
if ( ! this . refreshed . compareAndSet ( false , true )) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once" ) ;
}
}
看这里,如果当前 GenericApplicationContext
是刚创建的,那么 refreshed
的值一定是 false ,此时使用 CAS 设置 true 是成功的,下面的 throw ex
动作不会执行;而第二次再调用 refresh
刷新 ApplicationContext
时,进入到该方法时,CAS 不通过,无法刷新 BeanFactory
,最终抛出异常。
为什么 AbstractRefreshableApplicationContext
就没这事呢?因为 AbstractRefreshableApplicationContext
中本来就没这个 refreshed
的设计,而且人家在关闭 BeanFactory
的时候,直接把原来的 BeanFactory
扔了,不要了,那当然得支持刷新呀(不然 BeanFactory
都没了 ApplicationContext
就是一空壳)。
2.5 剩余动作
复制 // ......
// Let subclasses do some final clean-up if they wish...
// 给子类用的,然而子类没一个重写的
onClose() ;
// Reset local application listeners to pre-refresh state.
if ( this . earlyApplicationListeners != null ) {
this . applicationListeners . clear ();
this . applicationListeners . addAll ( this . earlyApplicationListeners );
}
// Switch to inactive.
this . active . set ( false );
}
剩余的动作就不重要了,而且也没什么实在东西,我们就不关注了。
至此,ApplicationContext
关闭,bean 的销毁动作也就全部结束了。
3. 总结
bean 对象在销毁时,由 **ApplicationContext**
发起关闭动作。销毁 bean 的阶段,由 **BeanFactory**
取出所有单实例 bean ,并逐个销毁。
销毁动作会先将当前 bean 依赖的所有 bean 都销毁,随后回调自定义的 bean 的销毁方法,之后如果 bean 中有定义内部 bean 则会一并销毁,最后销毁那些依赖了当前 bean 的 bean 也一起销毁。