内容介绍

在Spring框架中,你可以使用以下几种方法定义bean的初始化和销毁:

  1. 使用注解方式:

    • @PostConstruct:在bean的方法上添加@PostConstruct注解,该方法将在bean的依赖注入完成后立即执行,用于初始化操作。
    • @PreDestroy:在bean的方法上添加@PreDestroy注解,该方法将在容器销毁该bean之前执行,用于清理资源或执行一些必要的关闭操作。
  2. 实现接口方式:

    • InitializingBean接口:通过实现该接口,可以在bean的初始化阶段执行afterPropertiesSet()方法,用于初始化操作。
    • DisposableBean接口:通过实现该接口,可以在bean销毁之前执行destroy()方法,用于资源清理或关闭操作。
  3. 使用XML配置方式:

    • <bean>元素的init-method属性:在XML配置中,可以通过在<bean>元素中使用init-method属性来指定bean初始化时要调用的方法名。
    • <bean>元素的destroy-method属性:类似地,可以使用destroy-method属性指定bean销毁时要调用的方法名。

第一种方法将在BeanPostProcessor中实现,后面将编写文章专门介绍,本篇文章将介绍后两种实现方式。

针对第三种使用XML配置的方式,需要在BeanDefinition中增加属性initMethodName和destroyMethodName。初始化方法在AbstractAutowireCapableBeanFactory#invokeInitMethods执行。DefaultSingletonBeanRegistry中增加属性disposableBeans保存拥有销毁方法的bean,拥有销毁方法的bean在AbstractAutowireCapableBeanFactory#registerDisposableBeanIfNecessary中注册到disposableBeans中。

为了确保在虚拟机关闭之前执行销毁方法,使用AbstractApplicationContext类的registerShutdownHook()方法向虚拟机注册一个钩子方法。对于非web应用程序,需要手动调用该方法。另外,也可以通过手动调用ApplicationContext的close()方法来关闭容器。

当前bean的声明周期:

当前bean的声明周期

代码分支

GitHub - yihuiaa/little-spring at init-and-destroy-method剖析Spring源码,包括常见特性IOC、AOP、三级缓存等... Contribute to yihuiaa/little-spring development by creating an account on GitHub.https://github.com/yihuiaa/little-spring/tree/init-and-destroy-method

核心代码

BeanDefinition

添加initMethodNamedestroyMethodName属性,注意打印信息的打印顺序。

public class BeanDefinition {

    private String initMethodName;

    private String destroyMethodName;

    //getter and setter ...
}

DisposableBean接口

销毁方法的定义,bean通过实现该接口的方式实现销毁前执行destory方法;

public interface DisposableBean {
void destroy() throws Exception;
}

DisposableBeanAdapter

设计模式(适配其模式)的适配类,适配xml形式的销毁方式,当类没有实现DisposableBean接口时候,可以定义Bean自己的销毁方法,在spring的xml中配置销毁方法使其注册到BeanDefinition中生效。

package org.springframework.beans.factory.support;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean; import java.lang.reflect.Method; /**
* ● @author: YiHui
* ● @date: Created in 16:21 2023/6/25
*/
public class DisposableBeanAdapter implements DisposableBean {
private final Object bean;
private final String beanName;
private final String destroyMethodName; public DisposableBeanAdapter(Object bean, String beanName, String destroyMethodName) {
this.bean = bean;
this.beanName = beanName;
this.destroyMethodName = destroyMethodName;
} @Override public void destroy() throws Exception {
if(bean instanceof DisposableBean){
((DisposableBean)bean).destroy();
}
//DisposableBean的自定义的销毁方法命名为destroy时避免重复执行
if(StrUtil.isNotEmpty(destroyMethodName) && !((bean instanceof DisposableBean) && destroyMethodName.equals("destroy"))){
Method destroyMethod = ClassUtil.getPublicMethod(bean.getClass(), destroyMethodName);
if(destroyMethod == null){
throw new BeansException("Could not find destroy method [" + destroyMethodName + "] on bean [" + beanName + "]");
}
destroyMethod.invoke(bean);
}
} }

DefaultSingletonBeanRegistry

添加disposableBean的注册方法和存储他的容器与移除销毁的方法

/**
* ● @author: YiHui
* ● @date: Created in 16:03 2023/2/20
* ● @notes: 默认单例bean注册类
*/
public abstract class DefaultSingletonBeanRegistry implements SingletonBeanRegistry { private Map<String, Object> singletonObjects = new HashMap();
private Map<String, DisposableBean> disposableBeans = new HashMap(); @Override public Object getSingleton(String beanName) {
return singletonObjects.get(beanName);
} @Override public void addSingleton(String beanName, Object singletonObject) {
singletonObjects.put(beanName, singletonObject);
} protected abstract BeanDefinition getBeanDefinition(String beanName); public void registerDisposableBean(String beanName, DisposableBean bean) {
disposableBeans.put(beanName, bean);
} public void destroySingletons(){
List<String> beanNameList = new ArrayList<>(disposableBeans.keySet());
beanNameList.forEach(beanName ->{
DisposableBean disposableBean = disposableBeans.remove(beanName);
try {
disposableBean.destroy();
} catch (Exception e) {
throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
}
});
} }

AbstractAutowireCapableBeanFactory

自动注册的beanDefinition工厂类添加方法,支持注册有销毁方法的bean(即继承自DisposableBean或有自定义的销毁方法并在xml定义的Bean)。当用户没有实现DisposableBean接口但是注册了自己的销毁方法在xml时,将利用此方法将beanDefinition中的方法名称提取,通过上面定义的DisposableBeanAdapter的方式在创建bean的时候存入单例beanFactory的容器中。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements
AutowireCapableBeanFactory {
... @Override protected Object createBean(String name, BeanDefinition beanDefinition,Object[] args) {
Object bean = null;
try {
bean = createBeanInstance(name,beanDefinition,args);
//填充属性
applyPropertyValues(name,bean, beanDefinition);
//执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法
initializeBean(name, bean, beanDefinition);
}catch(Exception e){
throw new BeansException("Fail to instantiation",e);
}
//注册有销毁方法的bean
registerDisposableBeanIfNecessary(name, bean, beanDefinition); addSingleton(name,bean);
return bean;
} /**
* 支持注册有销毁方法的bean(即继承自DisposableBean或有自定义的销毁方法并在xml定义的Bean)
* @param beanName
* @param bean
* @param beanDefinition
*/
protected void registerDisposableBeanIfNecessary(String beanName,Object bean,BeanDefinition beanDefinition) {
if(bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
registerDisposableBean(beanName,new DisposableBeanAdapter(bean, beanName,beanDefinition.getDestroyMethodName()));
}
} ...
}

AbstractAutowireCapableBeanFactory

初始化方法的定义与调用

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements
AutowireCapableBeanFactory {
...
/**
* 执行bean的初始化方法
*
* @param beanName
* @param bean
* @param beanDefinition
* @throws Throwable
*/
protected void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
if(bean instanceof InitializingBean){
((InitializingBean)bean).afterPropertiesSet();
}
String initMethodName = beanDefinition.getInitMethodName();
if(StrUtil.isNotEmpty(initMethodName)){
Method initMethod = ClassUtil.getPublicMethod(bean.getClass(), initMethodName);
if(initMethod == null){
throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
}
initMethod.invoke(bean);
}
} ...
}

XmlBeanDefinitionReader

最后不要忘记在reader的bean读取方法中将xml中bean的初始和销毁方法名称注入BeanDefinition。

public class XmlBeanDefinitionReader  extends AbstractBeanDefinitionReader {

...
public static final String INIT_METHOD_ATTRIBUTE = "init-method";
public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method"; private void doLoadBeanDefinitions(InputStream inputStream){
//xml解析
String initMethodName = bean.getAttribute(INIT_METHOD_ATTRIBUTE);
String destroyMethodName = bean.getAttribute(DESTROY_METHOD_ATTRIBUTE);
...
beanDefinition.setInitMethodName(initMethodName);
beanDefinition.setDestroyMethodName(destroyMethodName);
...
} ...
}

测试

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <bean id="person" class="bean.DisposablePerson" init-method="customInitMethod" destroy-method="customDestroyMethod">
<!-- <bean id="person" class="bean.DisposablePerson" init-method="customInitMethod" >-->
<property name="name" value="yiHui"/>
<property name="car" ref="car"/>
</bean> <bean id="car" class="bean.Car">
<property name="name" value="Rolls-Royce"/>
</bean> </beans>

测试方法

    @Test
public void initAndDestroyMethodTest() {
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("classpath:spring.xml");
applicationContext.registerShutdownHook(); //或者手动关闭 applicationContext.close();
}

DisposablePerson

public class DisposablePerson extends Person implements DisposableBean, InitializingBean {
@Override public void destroy() throws Exception {
System.out.println("[ destroy ] :哦天呐!我要被干了");
} @Override public void afterPropertiesSet() throws Exception {
System.out.println("[ afterPropertiesSet ] :哦天呐!我被发现了");
} public void customInitMethod() {
System.out.println("customInitMethod : 我自己的初始化方法!");
} public void customDestroyMethod() {
System.out.println("customDestroyMethod: 我自己的销毁方法!");
}
}

测试结果

[ afterPropertiesSet ] :哦天呐!我被发现了
customInitMethod : 我自己的初始化方法!
[ destroy ] :哦天呐!我要被干了
customDestroyMethod: 我自己的销毁方法! Process finished with exit code 0

[源码系列:手写spring] IOC第十节:bean的初始化和销毁方法的更多相关文章

  1. Spring源码分析 手写简单IOC容器

    Spring的两大特性就是IOC和AOP. IOC Container,控制反转容器,通过读取配置文件或注解,将对象封装成Bean存入IOC容器待用,程序需要时再从容器中取,实现控制权由程序员向程序的 ...

  2. 《四 spring源码》手写springioc框架

    手写SpringIOCXML版本 /** * 手写Spring专题 XML方式注入bean * * * */ public class ClassPathXmlApplicationContext { ...

  3. 【Spring Framework】Spring注解设置Bean的初始化、销毁方法的方式

    bean的生命周期:创建---初始化---销毁. Spring中声明的Bean的初始化和销毁方法有3种方式: @Bean的注解的initMethod.DestroyMethod属性 bean实现Ini ...

  4. Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  5. Spring IOC - 控制反转(依赖注入) - 配置初始化和销毁的方法

    在Spring中如果某个bean在初始化之后,或销毁之前要做一些额外操作可以为该bean配置初始化和销毁的我方法,在这些方法中完成需要的功能. 实验: 通过断点调试模式,测试初始化方法和销毁方法的执行 ...

  6. 从零开始手写 spring ioc 框架,深入学习 spring 源码

    IoC Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理. 创作目的 使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过 ...

  7. 框架源码系列六:Spring源码学习之Spring IOC源码学习

    Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的  1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...

  8. 《四 spring源码》手写springmvc

    手写SpringMVC思路 1.web.xml加载  为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息.通过web.xml ...

  9. Spring源码 20 手写模拟源码

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

  10. 利用递归,反射,注解等,手写Spring Ioc和Di 底层(分分钟喷倒面试官)了解一下

    再我们现在项目中Spring框架是目前各大公司必不可少的技术,而大家都知道去怎么使用Spring ,但是有很多人都不知道SpringIoc底层是如何工作的,而一个开发人员知道他的源码,底层工作原理,对 ...

随机推荐

  1. 数字世界中的纸张——理解 PDF (转载)

    转载自 https://type.cyhsu.xyz/2018/09/understanding-pdf-the-digitalized-paper/ 引言 PDF 是我们打交道最多的文件格式之一.提 ...

  2. Java并发包常用类用法及原理

    com.java.util.concurrent包是java5时添加的,专门处理多线程提供的工具类 一.Atomic 二.Lock 三.BlockingQueue 四.BlockDeque 五.Con ...

  3. PDCA使用指南详解(史上最强)

    PDCA循环,一个老话题了,大家都知道要这么做,但在平时的生活和工作当中,你是否真的都这样做呢? 当你发现你面对的困难重重,寸步难行时,是否反思过是因为自己一开始的方法就不正确? 本文包涵了PDCA循 ...

  4. Wgpu图文详解(05)纹理与绑定组

    前言 什么是纹理? 纹理是图形渲染中用于增强几何图形视觉效果的一种资源.它是一个二维或三维的数据数组,通常包含颜色信息,但也可以包含其他类型的数据,如法线.高度.环境光遮蔽等.纹理的主要目的是为几何图 ...

  5. Linux7安装pacemaker+corosync集群-02--配置集群文件系统gfs2(dlm+clvmd)

    配置集群文件系统: 安装软件包: yum -y install   lvm2* gfs2* dlm* 1.安装rpm包 yum -y install   lvm2* gfs2* dlm* fence- ...

  6. Linux安装hive

    1.需要先安装hadoop以及mysql,参考其他文章 安装hive和hadoop都在hadoop用户目录下 2.安装hive,之前,先在mysql上创建一个hive,数据库,并在hive数据库中建立 ...

  7. Hetao P1031 萌萌题 题解 [ 蓝 ] [ 线性 dp ]

    萌萌题:一道结合了观察性质的线性 dp. 观察 我们先考虑极端情况:所有数相同,所有数降序排列两种情况. 对于所有数相同的情况,我们发现,最终可以合并出来的区间,最多只有 \(n \log n\) 个 ...

  8. DeepSeek模型技术动态引行业关注,企业生产系统API迁移需审慎评估

    在当今数字化浪潮中,人工智能技术迅猛发展,各类模型如雨后春笋般涌现,而 DeepSeek 模型凭借其独特的优势,在人工智能领域迅速崭露头角,成为备受瞩目的新星. DeepSeek 模型由杭州深度求索人 ...

  9. 一分钟搞定!CentOS 7.9上用Ansible自动化部署SQL Server 2019

    一分钟搞定!CentOS 7.9上用Ansible自动化部署SQL Server 2019 不熟悉整个流程的朋友可以先看之前的部署文章,手动部署一遍 一步步教你在CentOS 7.9上安装SQL Se ...

  10. 硅基流动最新邀请码:9MqV8tO4

    硅基流动最新邀请码:9MqV8tO4 硅基流动最新邀请码:9MqV8tO4