Spring 源码分析——Bean初始化和销毁

Spring 源码分析——Bean 初始化和销毁

本篇文章讨论 Spring 源码中的初始化和销毁操作,以及什么时候触发初始化和销毁函数。

对应 Github 源码完整文档:https://github.com/TyCoding/mini-spring/tree/main/docs/ioc/08-bean-init-destroy

前言

在之前我们有讲了 Bean 的实例化、初始化过程,以及在 Spring 上下文 Spring 上下文 ApplicationContext 中,Spring 是如何实现提前加载 Bean 的,这其中包含了有些 Bean 是被提前加载的(例如 Spring 容器本身需要的 Bean)、有些 Bean 是被延迟加载只有在注入时才会被初始化(例如业务中用@Component 或@Service 标记的 Bean)。

但是既然 Bean 的生命周期中存在实例化、初始化过程,就必然包含生命周期末尾的销毁过程,在之前的文章中我们知道 Spring 获取一个单例 Bean 的接口是SingletonBeanRegistry,而此接口的默认实现类是DefaultSingletonBeanRegistry,在这个实现类中包含了三级缓存处理逻辑。如果你仔细看能发现其中也包含了destroyXxx相关函数。

例如 DefaultSingletonBeanRegistry 中的destroySingletons函数:

public void destroySingletons() {
  if (logger.isTraceEnabled()) {
    logger.trace("Destroying singletons in " + this);
  }
  synchronized (this.singletonObjects) {
    this.singletonsCurrentlyInDestruction = true;
  }

  String[] disposableBeanNames;
  synchronized (this.disposableBeans) {
    disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
  }
  for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
    destroySingleton(disposableBeanNames[i]);
  }

  this.containedBeanMap.clear();
  this.dependentBeanMap.clear();
  this.dependenciesForBeanMap.clear();

  clearSingletonCache();
}

这是在 Spring 内部维护一个 Bean 的生命周期,在特定的时候由 Spring 内部去销毁 Bean 而调用的函数。

InitializingBean、DisposableBean

我们知道,在容器启动时 Spring 上下文 ApplicationContext 会预先通过refresh函数提前装配 Bean,这其中包含了 Spring 内部主动调用 BeanFactory#getBean()函数装配 Bean,并且在DefaultSingletonBeanRegistry内部提供了销毁 Bean 的函数。

但是这里要提到的是,Spring 提供了两个外部接口用于初始化和销毁 Bean,实现这两个接口的类将会在初始化时或者销毁时被 Spring 调用;注意:这两个接口并不是必须实现的,并且它是 Spring 给外部暴露的两个管理 Bean 生命周期的接口,是否实现取决于业务本身,如果项目中没有主动实现这两个接口(或者使用一些注解),那么 Spring 将不会执行这两个接口中的函数,而是在 Spring 容器内部去管理 Bean 的初始化和销毁操作。

public interface InitializingBean {
 void afterPropertiesSet() throws Exception;
}

public interface DisposableBean {
 void destroy() throws Exception;
}
  • InitializingBean 接口: 当一个 Bean 实现此接口,Spring 容器会在 bean 的属性设置完成后调用afterPropertiesSet()方法;通常用于执行某些初始化操作。
  • DisposableBean 接口: 当一个 Bean 实现此接口,Spring 会在 bean 销毁时调用destroy()方法;通常用于执行某些清理操作。

如何使用?

  1. 直接实现这两个接口并标记为 bean

  2. 使用注解:@PostConstruct 标记方法,在 bean 创建后立即执行用于初始化操作;@PreDestroy 标记方法,在 bean 被销毁之前执行用于释放资源操作;

    public class MyBean {
        @PostConstruct
        public void init() {
            // 初始化逻辑
        }

        @PreDestroy
        public void cleanup() {
            // 清理逻辑
        }
    }
  1. 使用 XML 配置的init-methoddestroy-method

    <bean id="myBean" class="com.example.MyBean" init-method="initMethod" destroy-method="destroyMethod">
        <!-- 设置 bean 的属性 -->
    </bean>

close

在之前提到 Spring 上下文 ConfigurableApplicationContext 中存在一个refresh函数用于在容器初始化时调用,同样还存在一个close函数在容器关闭时调用:

Spring 源码分析——Bean初始化和销毁
image-20230509133751684

close接口实现在AbstractApplicationContext

@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
      }
    }
  }
}

protected void doClose() {
  ......

  // Destroy all cached singletons in the context's BeanFactory.
  destroyBeans();

  // Close the state of this context itself.
  closeBeanFactory();

  // Let subclasses do some final clean-up if they wish...
  onClose();

  ......
}

protected void destroyBeans() {
  getBeanFactory().destroySingletons();
}

可以看到,其实 Spring 内部在容器关闭时会通过执行close函数进而调用DefaultSingletonBeanRegistry#destroySingletons() 统一销毁单例 Bean 释放资源。

Spring 容器默认在关闭容器是销毁所有 bean 实例,其中包含几个过程:

  1. 关闭 ApplicationContext:Spring 容器收到关闭信号后,会先关闭 ApplicationContext 上下文本身;
  2. 销毁单例 bean:Spring 容器会依次销毁所有单例 bean 实例(包括使用@Component、@Service、@Repository 等注解定义的 Bean),并清理相关资源;
  3. 销毁其他 bean:Spring 容器会依次销毁所有非单例 bean 实例,也就是每次请求都创建一个新实例的 prototype bean;

Spring 源码专栏

此专栏将从 Spring 源码角度整体分析 Spring 设计思路以及常见的面试题

配套作者的手写 Spring 的项目:https://github.com/TyCoding/mini-spring 。该项目中包含各个阶段的开发文档,有关 Spring 源码更详细的分析测试文档请查阅:https://github.com/TyCoding/mini-spring/tree/main/docs

联系我

  • 个人博客:http://tycoding.cn/
  • GitHub:https://github.com/tycoding
  • 微信公众号:程序员涂陌
  • QQ 交流群:866685601


原文始发于微信公众号(程序员涂陌):Spring 源码分析——Bean初始化和销毁

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145587.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!