开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第22天,点击查看活动详情
前言
我们在上一篇文章中讲述了如何设置Spring Bean对象的资源释放方法,那么有一个疑问就是,这些配置好的destory-method是如何被调用的呢?
为了解开这个疑惑,我们不得不深入Spring的源码来分析一下;
容器关闭
Spring Bean之所以被销毁,其实与Spring容器本身被关闭有关系,我们可以看到在Spring容器调用close()方法时,就会触发Bean的销毁动作,可以查看AbstractApplicationContext.close()方法:
protected void doClose() {
// 容器还是启动状态并且没有被关闭的情况下才能执行下面的代码
if (this.active.get() && this.closed.compareAndSet(false, true)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Closing " + this);
}
if (!NativeDetector.inNativeImage()) {
LiveBeansView.unregisterApplicationContext(this);
}
// 发布ContextClosedEvent事件
try {
this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this)));
} catch (Throwable var3) {
this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3);
}
// 关闭lifecycle bean
if (this.lifecycleProcessor != null) {
try {
this.lifecycleProcessor.onClose();
} catch (Throwable var2) {
this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2);
}
}
// 执行Bean配置的销毁方法
this.destroyBeans();
// 关闭BeanFactory
this.closeBeanFactory();
this.onClose();
// 处理earlyApplicationListeners监听器列表
if (this.earlyApplicationListeners != null) {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// 设置为非活跃状态
this.active.set(false);
}
}
在Spring容器执行close()时,大概会做以下几件事情:
1.发布一个
ContextClosedEvent事件,表示Spring容器即将要关闭了;2.处理
lifecycle bean,这些Bean只在容器启动和关闭的时候会被调用相关的方法;3.执行
Bean的销毁方法,也就是destory-method;xml配置的destory-method、@PreDestory注解标注的方法以及实现了DisposableBean接口的destory()方法,都属于要被执行的方法;4.关闭
BeanFactory;5.处理
earlyApplicationListeners监听器列表;6.修改
Spring容器运行状态;
我们最主要需要关心的就是第三步,它的原理就是把所有实现了DisposableBean接口的Bean都取出来执行一遍destory()方法:
protected void destroyBeans() {
this.getBeanFactory().destroySingletons();
}
public void destroySingletons() {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Destroying singletons in " + this);
}
synchronized(this.singletonObjects) {
this.singletonsCurrentlyInDestruction = true;
}
// 取出所有实现DisposableBean接口的Bean名称
String[] disposableBeanNames;
synchronized(this.disposableBeans) {
disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
}
// 依次调用destory()方法
for(int i = disposableBeanNames.length - 1; i >= 0; --i) {
this.destroySingleton(disposableBeanNames[i]);
}
this.containedBeanMap.clear();
this.dependentBeanMap.clear();
this.dependenciesForBeanMap.clear();
this.clearSingletonCache();
}
到这里就已经很明朗了,Spring容器在销毁前会把所有实现了DisposableBean接口的Bean都执行一下destory()方法,这样就实现了Bean的资源释放的功能了;
销毁方法的注入
按照上面的源码分析,Spring容器销毁前只会执行DisposableBean的销毁方法,那么通过xml配置文件或@PreDestory注解设置的销毁方法是不是就无效了呢?
在Spring Bean的生命周期中,我们可以在看到AbstractAutowireCapableBeanFactory.doCreateBean()方法的最后一步调用了registerDisposableBeanIfNecessary()方法:
try {
// 注册销毁方法
this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
} catch (BeanDefinitionValidationException var16) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
}
我们来看看里面做了啥:
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = System.getSecurityManager() != null ? this.getAccessControlContext() : null;
// requiresDestruction()方法检查是否指定销毁方法
if (!mbd.isPrototype() && this.requiresDestruction(bean, mbd)) {
if (mbd.isSingleton()) {
// 注册销毁方法,通过DisposableBeanAdapter做了一层包装
this.registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, this.getBeanPostProcessorCache().destructionAware, acc));
} else {
Scope scope = (Scope)this.scopes.get(mbd.getScope());
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
}
// 和上面一样通过DisposableBeanAdapter包装
scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(bean, beanName, mbd, this.getBeanPostProcessorCache().destructionAware, acc));
}
}
}
this.requiresDestruction(bean, mbd)方法可以解开我们的疑惑:
protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
return bean.getClass() != NullBean.class &&
// 必须有销毁方法,比如实现了DisposableBean接口,或者BeanDefinition配置了destory-method,或者实现了AutoCloseable接口的close()方法
(DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||
// 或者被注解@PreDestory注解标注了方法
// 被@PreDestory注解标注的原理可以查看CommonAnnotationBeanPostProcessor类
this.hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(bean, this.getBeanPostProcessorCache().destructionAware));
}
也就是说,所有包含销毁方法的bean都会被包装成DisposableBeanAdapter,因为DisposableBeanAdapter实现了DisposableBean接口,所以在最后Spring容器关闭时只需要统一调用DisposableBean接口的destory()方法即可;