首页 > 其他分享 >浅谈 Spring Bean 的生命周期

浅谈 Spring Bean 的生命周期

时间:2023-05-31 14:34:14浏览次数:40  
标签:初始化 调用 浅谈 Spring beanName 接口 Bean 方法

一、Bean 的生命周期概述

区别于普通的 Java 对象需要通过 new 创建对象,Spring 的 Bean 由 IoC 容器进行实例化、组装以及管理的。也就是说 Bean 的生命周期完全由 IoC 容器控制。

Spring 容器只能管理 单例(singleton) 作用域的 Bean 的完整生命周期,对于 原型(prototype) 作用域的 Bean,Spring 容器只创建 bean 的实例后便会返回给用户,剩余的生命周期由用户控制。所以 Bean 的生命周期主要指的是 singleton 作用域 的 Bean。

为什么要了解 Bean 的生命周期?

通过了解 Bean 的生命周期可以了解 Bean 是什么时候被创建的,什么时候初始化,什么时候被销毁以及整个 Bean 的生命周期中哪些阶段可以使用哪些增加方法。

这样在实际开发中,可以利用 Bean 的生命周期的指定时刻完成一些自定义的操作。同时面试中 Spring Bean 的生命周期也是常问的一个内容,所以很有必要了解一下。

Bean 的生命周期主要包括四个部分

  1. 实例化(Instantiation)
  2. 属性赋值(Populate)
  3. 初始化(Initialization)
  4. 销毁(Destruction)

以 AbstractApplicationContext 的 refresh() 方法为入口,AbstractAutowireCapableBeanFactory 的 doCreateBean 方法完成对 Bean 的实例化、属性注入以及初始化。(具体流程可以看 3.2 的具体流程梳理)

 

以AbstractApplicationContext 的 close() 方法为入口,在 DisposableBeanAdapter 的 destory() 方法完成对 Bean 的销毁。(具体流程可以看 3.2 的具体流程梳理)

 


二、相关接口及方法

Bean 的生命周期中使用到了 Aware 类型的相关接口、InitializingBean/DisposableBean 接口以及一些起到增强作用的接口。了解这些接口可以帮助大家更好地了解 Bean 的生命周期以及更好地拓展定制自己的 Bean 对象。

2.1 各种 Aware 类型的接口

实现 Aware 接口的目的是让程序可以拿到 Spring 容器的当前的运行环境(如当前 Bean 的名称、当前的 BeanFactory、当前的 ApplicationContext 等等资源)。这类接口的调用时机是在 Bean 实例化、属性注入之后,初始化之前。

接下来列出一些常用的子接口,有兴趣的可以从源码中查看还有哪些接口(基本都是见名知意):

BeanNameAware 接口

接口中只有一个方法 void setBeanName(String name) 用于获取当前 Bean 的名称。

 

BeanFactoryAware 接口

接口中只有一个方法 void setBeanFactory(BeanFactory beanFactory) 用于获取当前的 BeanFactory。

 

ApplicationContextAware 接口

接口中只有一个方法 void setApplicationContext(ApplicationContext applicationContext) 用于获取当前的 ApplicationContext。

 

2.2 InitializingBean/DisposableBean 接口

实现InitializingBean/DisposableBean 接口能够实现自定义初始化方法以及销毁方法。这两个接口的调用时机分别在初始化阶段与销毁阶段。

InitializingBean 接口

接口中只有一个方法 void afterPropertiesSet() 用于自定义初始化行为,在属性注入阶段后执行。

 

DisposableBean 接口

接口中只有一个方法 void destroy() 用于自定义销毁行为,在销毁阶段执行。

 

除了接口实现方式外,还有两种可以实现自定义初始化以及销毁的方式:

  • 使用 XML 配置文件 或者 Java 配置类 对 Bean 中的 init-method 与 destroy-method 属性进行配置(基于 Bean 自身的初始化和销毁)
  • 使用 注解 @PostConstruct 与 @PreDestroy 修饰方法(作用于servlet生命周期的注解)

2.3 增强方法

BeanFactoryPostProcessor 接口

实现该接口可以增强的方面是:在 BeanFactory 已经初始化而 Bean 实例化之前调用该接口的方法可以修改或添加 Bean 的定义。所以该接口的调用时机是在 Bean 实例化之前。

接口中只有一个方法 void postProcessBeanFactory(ConfigurableListableBeanFactory var1) 用于在 Bean 实例化之前修改 Bean 的定义。

 

BeanPostProcessor 接口

实现该接口能增强的方面是:在 Bean 实例化后,在初始化阶段的前后自定义行为。可以根据接口方法中的参数来筛选需要自定义行为的 Bean。该接口的调用时机是在 初始化阶段的前后。

该接口有两个方法分别在初始化前和初始化后执行:

  • postProcessBeforeInitialization(Object bean, String beanName):初始化前调用
  • postProcessAfterInitialization(Object bean, String beanName):初始化后调用

 


InstantiationAwareBeanPostProcessor 接口

实现该接口能增强的方面是:在目标 Bean 实例化的前后可以自定义行为以及在属性注入前可以修改 Bean 的属性设置。

该接口有三个方法(其实还有两个来自于继承 BeanPostProcessor 接口的方法):

  • postProcessBeforeInstantiation(Class<?> beanClass, String beanName):在 Bean 实例化之前调用
  • postProcessAfterInstantiation(Object bean, String beanName):在 Bean 实例化之后调用
  • postProcessProperties(PropertyValues pvs, Object bean, String beanName):在 Bean 实例化之后,属性注入之前调用

 

三、浅谈生命周期的具体流程

3.1 通过例子展示具体流程

通过一个例子来体现 Bean 的生命周期 的具体流程以及相关接口对应的切入时机

MyBean 类

/**
 * Bean对象
 * @author 单程车票
 */
public class MyBean implements InitializingBean, DisposableBean, BeanNameAware {

    private Integer id;

    public MyBean(Integer id) {
        this.id = id;
        System.out.println("过程3(实例化阶段中):调用构造方法,当前id为" + this.id + "(可以发现id此时并不是配置文件的1001,而是增强方法修改后的1002)");
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
        System.out.println("过程6(属性注入阶段中):属性注入阶段,注入后id为" + this.id + "(可以发现id此时并不是配置文件的1003,而是增强方法修改后的1004)");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("过程7(属性注入阶段后):获取当前Bean的名称:" + name);
    }

    public void myInit() {
        System.out.println("过程10(初始化阶段中):调用Bean自身属性 init-method 的初始化方法");
    }

    public void myDestroy() {
        System.out.println("过程13(销毁阶段中):调用Bean自身属性 destroy-method 的销毁方法");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("过程9(初始化阶段中):调用 InitializingBean接口 的初始化方法");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("过程12(销毁阶段中):调用 DisposableBean接口 的销毁方法");
    }
}

实现 BeanFactoryPostProcessor 接口

/**
 * 对实例化前进行增强
 * @author 单程车票
 */
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("myBean");
        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        constructorArgumentValues.addIndexedArgumentValue(0, 1002);
        // 将原本配置文件配置的值1001改为1002(这里使用了ConstructorArgumentValues有兴趣的可以自行了解一下)
        beanDefinition.setConstructorArgumentValues(constructorArgumentValues);
        System.out.println("过程1(实例化阶段前): 调用 BeanFactoryPostProcessor.postProcessBeanFactory 方法进行增强,将id值改为1002");
    }
}

实现 BeanPostProcessor 接口

/**
 * 对初始化阶段前后进行增强
 * @author 单程车票
 */
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("myBean".equals(beanName)) {
            System.out.println("过程8(属性注入阶段后,初始化阶段前):调用 BeanPostProcessor.postProcessBeforeInitialization 方法进行增强");
        }
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("myBean".equals(beanName)) {
            System.out.println("过程11(初始化阶段后):调用 BeanPostProcessor.postProcessAfterInitialization 方法进行增强");
        }
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

实现
InstantiationAwareBeanPostProcessor 接口

/**
 * 实现InstantiationAwareBeanPostProcessor接口
 * @author 单程车票
 */
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if ("myBean".equals(beanName)) {
            System.out.println("过程2(实例化阶段前):调用 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation 方法进行增强");
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if ("myBean".equals(beanName)) {
            System.out.println("过程4(实例化阶段后):调用 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation 方法进行增强");
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if ("myBean".equals(beanName)) {  // 对 Bean 再次修改 id 属性
            PropertyValue propertyValue = pvs.getPropertyValue("id");
            assert propertyValue != null;
            propertyValue.setConvertedValue(1004);
            System.out.println("过程5(实例化阶段后,属性注入阶段前):调用 InstantiationAwareBeanPostProcessor.postProcessProperties 方法进行增强,将id属性改为1004");
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);
    }
}

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 这里需要注意加上 Bean自身的初始化方法和销毁方法 -->
    <bean id="myBean" class="com.xqsr.springtest.bean.MyBean" init-method="myInit" destroy-method="myDestroy">
        <!-- 这里需要注意构造器(实例化)的id值为1001 -->
        <constructor-arg name="id" value="1001"/>
        <!-- 这里需要注意setter(属性注入)的id值为1001 -->
        <property name="id" value="1003"/>
    </bean>

    <bean class="com.xqsr.springtest.bean.MyBeanFactoryPostProcessor"/>
    <bean class="com.xqsr.springtest.bean.MyBeanPostProcessor"/>
    <bean class="com.xqsr.springtest.bean.MyInstantiationAwareBeanPostProcessor"/>

</beans>

测试类

/**
 * 测试类
 * @author 单程车票
 */
public class Application {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        MyBean bean = (MyBean) applicationContext.getBean("myBean");
        ((AbstractApplicationContext) applicationContext).close();
    }
}

执行结果

 

3.2 具体流程梳理

通过上面的例子可以梳理出下面这张 Bean 的生命周期 具体流程图

 

接下来从源码的角度对流程图的每一个部分进行剖析

从源码看 BeanFactoryPostProcessor # postProcessBeanFactory() 在实例化前执行

 

 

通过从 refresh() 入口进入按照上面图中的流程可以找到调用 postProcessBeanFactory() 的方法,该方法是在 invokeBeanFactoryPostProcessors(beanFactory) 进入的,而实例化阶段是从 finishBeanFactoryInitialization(beanFactory) 进入的,所以可以说明 BeanFactoryPostProcessor # postProcessBeanFactory() 要先于实例化执行。

 

从源码看
InstantiationAwareBeanPostProcessor # postProcessBeforeInstantiation() 在实例化前执行

 

 

通过上面的流程图可以找到调用 postProcessBeforeInstantiation() 的方法是在 resolveBeforeInstantiation(beanName, mbdToUse) 进入的,而实例化阶段是在 doCreateBean(beanName, mbdToUse, args) 进入的,所以可以说明 postProcessBeforeInstantiation() 要先于实例化执行。

 

从源码看 实例化执行位置 与 属性注入执行位置

 

实例化阶段发生在 AbstractAutowireCapableBeanFactory 的 createBeanInstance() 方法中。

属性注入阶段发生在 AbstractAutowireCapableBeanFactory 的 populateBean() 方法中。

 

从源码看
InstantiationAwareBeanPostProcessor # postProcessAfterInstantiation() 与 InstantiationAwareBeanPostProcessor # postProcessPropertyValues() 属性注入阶段前执行

 

通过上面的流程图可以找到 postProcessAfterInstantiation() 与 postProcessPropertyValues() 在 populateBean() 属性注入方法内被调用。

 

  • postProcessAfterInstantiation() 方法如果找到指定的 bean 就会返回 false,会直接返回不再执行后续方法内容,也就是说后续的属性填充和依赖注入就不会被执行,所以可以看出 postProcessAfterInstantiation() 执行在属性注入阶段前。
  • postProcessPropertyValues() 方法同样发生在 属性填充 之前,如果返回null则不会执行后续的属性填充,如果不会为null,说明有额外添加的属性需要填充,后续方法会执行属性填充。

从源码看 初始化阶段及其前后发生的调用

 

根据上面的流程图可以找到初始化阶段进入的入口 initializeBean() 方法,具体代码如下图:

 

  1. 先看 Aware 接口的调用
  2. 除了上面 BeanXXXAware 接口的调用,如 ApplicationContextAware 接口的调用是发生在 applyBeanPostProcessorsBeforeInitialization 方法中的,也就是是通过 BeanPostProcessor的postProcessBeforeInitialization() 实现调用的,具体的可以自己跟一下源码。
  3. BeanPostProcessor 的两个实现方法
  4. 这两个实现方法分别通过 applyBeanPostProcessorsBeforeInitialization 与 applyBeanPostProcessorsAfterInitialization 方法调用,也可以看出一个在初始化前一个在初始化后执行。
  5. invokeInitMethods 初始化方法
  6. invokeInitMethods 初始化方法中调用了 InitializingBean 的自定义初始化方法 与 Bean自身属性中的 init-method 指定的方法。

从源码看 销毁阶段

 

根据上面的流程图最终会找到 DisposableBeanAdapter 的 destory() 方法,具体代码如下:

 

通过 destory() 方法也可以看到销毁阶段先执行 DisposableBean 的销毁方法,再执行 Bean 自身属性 destory-method 指定的方法。

标签:初始化,调用,浅谈,Spring,beanName,接口,Bean,方法
From: https://www.cnblogs.com/lilinwei/p/17446019.html

相关文章

  • Could not autowire. No beans of 'AddressBookService' type found.
    错误:错误原因:Service实现类未继承Service接口解决方法: ......
  • SpringBoot 设置HTTP Status Code
    1HTTPStatusCodeHTTP请求响应的内容有很多,包括Body、Cookies、Headers和Status。我们最常用的是Body、其次Headers、Cookies。而HTTPStatusCode关注得最少。1.1HTTPStatusCode分类分类描述1**信息,服务器收到请求,需要请求者继续执行操作2**成功,操作被成功接......
  • springboot2到spring3的升级变化
    1.自动配置的变化springboot2引入依赖后不需要手动配置属性文件,spingboot3引入依赖后还需手动配置属性文件比如当我使用springboot3.1.0版本加入mybatis依赖生成springboo项目后,在pom文件加入druid连接池包的依赖,发现不管加哪个版本的druid,在接口访问数据库时一直用的还是sping......
  • java springboot 项目中静态资源无法访问的问题
    默认resource/static目录下的静态html文件无法访问,需要配置。您可以在SpringBoot的配置类中添加以下内容来手动配置静态资源目录:@ConfigurationpublicclassWebMvcConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddResourceHandlers(ResourceHan......
  • SpringBoot定义优雅全局统一Restful API 响应框架五
    闲话不多说,继续优化全局统一RestfulAPI响应框架做到项目通用接口可扩展。如果没有看前面几篇文章请先看前面几篇SpringBoot定义优雅全局统一RestfulAPI响应框架SpringBoot定义优雅全局统一RestfulAPI响应框架二SpringBoot定义优雅全局统一RestfulAPI响应框架三Sp......
  • 阿里云验证码短信功能---SpringBoot项目
    阿里云官网:https://www.aliyun.com/activity/2023caigouji/shangyuncaigouji?utm_content=se_1013408957准备工作注册阿里云账号申请AccessKeyID和AccessKeySecret搜索“短信服务SMS”,选择“免费开通”即可选择国内消息,申请签名管理和模板管理准备完成后我们可以获取Access......
  • Spring:Formatter 和 ConversionService 的区别?
    在Spring框架中,Formatter和ConversionService是两个独立的概念,并没有直接的继承关系。Formatter接口和ConversionService接口是在不同的包中定义的,它们有着不同的目的和功能。Formatter接口位于org.springframework.format包中,用于格式化和解析字段值,并提供了本地化、格式化选项......
  • SpringBoot项目中使用拦截器进行请求的拦截
    在没有拦截器拦截前端请求的情况下,即使用户不进行登录,却依然能够进入系统。这显然是不合理的。这里使用拦截器对其请求进行一个拦截并且过滤。将那些需要需要用户登录才能够展示的界面进行一个拦截,如果用户没有登录,就需要跳转到登录界面进行登录。没有拦截器的效果展示此时我们......
  • SpringBoot集成kafka全面实战
    一、前戏1、在项目中连接kafka,因为是外网,首先要开放kafka配置文件中的如下配置(其中IP为公网IP),advertised.listeners=PLAINTEXT://112.126.74.249:90922、在开始前我们先创建两个topic:topic1、topic2,其分区和副本数都设置为2,用来测试,[root@iZ2zegzlkedbo3e64vkbefZ~]#cd/......
  • spring boot 集成 swagger 空指针异常
    刚开始使用的是2.6.4的springboot集成的是swagger3,启动时报npe百度了一下可能是版本不兼容,说swagger3适用2.4以上,我心想这也没错哇,,又百度了一下说版本高也不行只能是2.5.7以下原文链接:https://blog.csdn.net/qq_53860947/article/details/124411891 2023-05-18......