前言

本系列前面讲解了Spring的bean定义、bean实例化、bean初始化等生命周期。这些步骤使我们能够了解bean从创建到准备好使用所经历的过程。但是,除了这些步骤,bean的销毁也是非常重要的一步。在本系列的最后,我们将深入探讨bean的销毁过程,包括在什么情况下会发生销毁、销毁的顺序以及如何在bean销毁之前执行一些清理任务等。通过学习bean的销毁过程,我们将更全面地了解Spring的bean生命周期。

在Spring中,有多种方式可以销毁bean。其中一种方式是在应用程序关闭时显式地调用applicationContext.close()方法来关闭容器。这个方法将会销毁所有还没有被销毁的bean。

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

applicationContext.close();

实现DisposableBean接口

实现DisposableBean接口是一种销毁bean的简单方式。当bean容器关闭时,Spring会调用DisposableBean的destroy()方法来销毁bean。以下是一些示例代码:

import org.springframework.beans.factory.DisposableBean;
@Component
public class MyBean implements DisposableBean { @Override
public void destroy() throws Exception {
// 在这里清理资源
}
}

使用@PreDestroy注解

使用@PreDestroy注解是另一种简单的方式来销毁bean。当bean容器关闭时,Spring会调用使用@PreDestroy注解的方法来销毁bean。以下是一些示例代码:

import javax.annotation.PreDestroy;
@Component
public class MyBean { @PreDestroy
public void cleanUp() throws Exception {
// 在这里清理资源
}
}

registerDisposableBeanIfNecessary

registerDisposableBeanIfNecessary()方法是一个非常重要的方法,它是在bean创建后进行处理bean销毁逻辑的前提。在Spring的AbstractBeanFactory类中,该方法会检查当前bean是否实现了DisposableBean接口或者@PreDestroy注解,如果是的话,就会将该bean添加到一个DisposableBeanAdapter对象中,该对象会在bean销毁时被调用以执行销毁任务。这个过程是在bean销毁之前执行的,以确保正确关闭应用程序。

    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
if (mbd.isSingleton()) {
// Register a DisposableBean implementation that performs all destruction
// work for the given bean: DestructionAwareBeanPostProcessors,
// DisposableBean interface, custom destroy method.
registerDisposableBean(beanName, new DisposableBeanAdapter(
bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
}
else {
// A bean with a custom scope...
Scope scope = this.scopes.get(mbd.getScope());
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
}
scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(
bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
}
}
}

我大概讲下这个方法requiresDestruction

    protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||
(hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(
bean, getBeanPostProcessorCache().destructionAware))));
}
  1. DisposableBeanAdapter.hasDestroyMethod:校验是否实现了DisposableBean或者AutoCloseable接口,如果没有的话,再查看是否bean定义的destroyMethodName属性是(inferred),如果是的话,那么直接找这个类是否有close方法没有的话再找shutdown方法
  2. DisposableBeanAdapter.hasApplicableProcessors:是否有@PreDestroy注解

DisposableBeanAdapter

DisposableBeanAdapter对象是一个适配器,用于在销毁bean时执行必要的处理。它会将DisposableBean接口或@PreDestroy注解的方法转换为一个回调方法,以便在bean销毁时执行。这种适配器模式允许非标准的bean销毁方法与Spring框架协同工作。

在将DisposableBeanAdapter对象添加到一个DisposableBeanRegistry对象中时,Spring会将该对象添加到一个bean销毁的注册表中。当需要销毁所有bean时,Spring就会从该注册表中获取所有需要销毁的bean,并按照正确的顺序执行销毁任务。这样就可以确保应用程序的正确关闭。

destroySingleton

当Spring程序关闭时,会调用destroyBeans方法,这里我们分析关键部分代码:

    public void destroySingleton(String beanName) {
// Remove a registered singleton of the given name, if any.
// 先从单例池中移除掉
removeSingleton(beanName); // Destroy the corresponding DisposableBean instance.
DisposableBean disposableBean;
synchronized (this.disposableBeans) {
disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
}
destroyBean(beanName, disposableBean);
}
  1. removeSingleton:先从单例池中移除掉
  2. this.disposableBeans.remove:这里返回的是我们之前调用registerDisposableBeanIfNecessary方法添加进去的DisposableBeanAdapter适配器
  3. destroyBean:直接销毁bean,这里注意一个小点就是如果当前bean被其他bean依赖了,那么先移除销毁其他Bean,然后就是调用适配器的destroy方法

总结

非常感谢您对 Spring 生命周期系列文章的关注和支持,我们在过去一个月中深入了解了 Spring 框架中 Bean 的生成、初始化、后置处理和销毁等过程,对于理解 Spring 框架的原理和机制非常有帮助。我们总结一下Spring到底做了那些事情将bean从生成到销毁的全过程:

  1. 项目启动时,ClassPathBeanDefinitionScanner扫描得到所有BeanDefinition,由于ACM技术所以此时beanclass属性为String类型的bean的名称
  2. 获取合并后的BeanDefinition
  3. beanClass开始真正的被加载替换原有String类型的bean的名称
  4. 调用实例化前处理方法applyBeanPostProcessorsBeforeInstantiation
  5. 通过构造方法创建Bean实例
  6. 后置处理合并后的BeanDefinition,调用postProcessMergedBeanDefinition(寻找注入点)
  7. 调用实例化后处理方法postProcessAfterInstantiation
  8. 开始进行属性注入:postProcessProperties
  9. 调用初始化前处理方法:applyBeanPostProcessorsBeforeInitialization
  10. 进行初始化:invokeInitMethods,会调用指定init方法或者afterPropertiesSet方法
  11. 调用初始化后处理方法:applyBeanPostProcessorsAfterInitialization(AOP)
  12. 容器关闭时,走bean的销毁逻辑,即今天所讲

这里面有很多逻辑流程我都在单独的文章中有细讲,比如FactoryBean、PropertyValues等等,由于是总结所以就不全写出来了,也希望大家可以好好理解Spring源码,下一步,我们将会着重讲解 Bean 的属性依赖注入。

Spring源码:Bean生命周期(终章)的更多相关文章

  1. Spring源码-Bean生命周期总览

  2. spring(二、bean生命周期、用到的设计模式、常用注解)

    spring(二.bean生命周期.用到的设计模式.常用注解) Spring作为当前Java最流行.最强大的轻量级框架,受到了程序员的热烈欢迎.准确的了解Spring Bean的生命周期是非常必要的. ...

  3. Spring(四)之Bean生命周期、BeanPost处理

    一.Bean 生命周期 Spring bean的生命周期很容易理解.当bean被实例化时,可能需要执行一些初始化以使其进入可用状态.类似地,当不再需要bean并从容器中移除bean时,可能需要进行一些 ...

  4. Spring事务,Bean生命周期

    一.事务相关: 1.Spring事务基于Spring AOP切面编程: 2.AOP基于代理模式,得到需要开启事务的代码的代理对象: 3.而没有开启事务的Service方法里调用了开启事务 @Trans ...

  5. spring源码-bean之增强初始化-3

    一.ApplicationContext的中文意思是“应用上下文”,它继承自BeanFactory接口,除了包含BeanFactory的所有功能之外,在国际化支持.资源访问(如URL和文件).事件传播 ...

  6. 【Spring系列】- Bean生命周期底层原理

    Bean生命周期底层原理 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 前言 上次学到动 ...

  7. Spring源码--Bean的管理总结(一)

    前奏 最近看了一系列解析spring管理Bean的源码的文章,在这里总结下,方便日后复盘.文章地址https://www.cnblogs.com/CodeBear/p/10336704.html sp ...

  8. TOMCAT源码分析——生命周期管理

    前言 从server.xml文件解析出来的各个对象都是容器,比如:Server.Service.Connector等.这些容器都具有新建.初始化完成.启动.停止.失败.销毁等状态.tomcat的实现提 ...

  9. 《Spring源码深度解析》第二章 容器的基本实现

    入门级别的spring配置文件 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi=&q ...

  10. spring源码-ioc容器周期

    Spring容器的refresh 创建刷新:   1-prepareRefresh刷新前的预处理: initPropertySources 初始化一些属性配置,原来是空的,子类自定义的属性设置方法 g ...

随机推荐

  1. Python学习笔记--SQL数据

    SQL 本人受到Java的影响,数据库的话,就不按照教程走了,我就直接使用的是Navicat软件的数据库啦! SQL支持注释: 两种单行注释(-- 和# ),和一种多行注释(/* */) 基础的使用语 ...

  2. 代码大全_V2(1,2章笔记)

    译序 这本书讲什么 代码大全 原名叫 code complete,它是什么,又不是什么? 不是IDE中的代码自动补全功能 不是软件源代码 "大全" 是 "编码完成&quo ...

  3. Jetson Xavier NX 试玩 (二)

    Jetson Xavier NX 试玩 (二) Hello AI World Inference 人工智能推理模型 0 前言 想玩一玩 jetson 的人工智能功能,官方的 instructional ...

  4. 树状分级框架UI实例

    树状分级框架UI实例:(内容参考https://zhuanlan.zhihu.com/p/108485875) #coding:utf8 #!/usr/bin/env python #@author: ...

  5. 使用EFCore的Code First和MySql数据库迁移

    1. 感慨一下 随着.net core的持续更新和升级,至少对于从事.net开发的人员和即将踏入这个领域的人来说,我相信大家的热情还是持续高涨的.国内的.net开发生态相比于之前来说,还是大有所好转的 ...

  6. .NET应用系统的国际化-多语言翻译服务

    上篇文章我们介绍了 .NET应用系统的国际化-基于Roslyn抽取词条.更新代码 系统国际化改造整体设计思路如下: 提供一个工具,识别前后端代码中的中文,形成多语言词条,按语言.界面.模块统一管理多有 ...

  7. springboot实现验证码功能

    实现验证码功能 先在utils包下创建一个ValidateImageCodeUtils.class package com.wfszmg.demo.utils; import javax.imagei ...

  8. 大数据面试——Flink

    一.公司怎么提交的实时任务,有多少 Job Manager.TaskManager 是多少 我们使用 yarn session 模式提交任务:另一种方式是每次提交都会创建一个新的 Flink集群,为每 ...

  9. DevOps 在未来将如何演进?丨行业观察

    自2007年 DevOps 这一概念推出以来,越来越多企业开始将开发和运维团队结合在一起,以加快部署速度,提高软件开发生命周期的效率和协作.但是,诸多因素都会对 DevOps 是否成功产生影响,例如组 ...

  10. vue指令之属性指令

    目录 属性指令 示例 属性指令 标签上的属性可以绑定变量,变量变化,属性也会变化 # 什么是属性?比如: href/src/name/value/class/style... 语法: v-bind:属 ...