【Spring笔记04】Spring中Bean的生命周期及Bean的后置处理器

news2024/10/6 20:33:58

这篇文章主要介绍的是Spring框架中Bean的生命周期,Bean的后置处理器、以及多个后置处理器的先后执行顺序。 

目录

一、生命周期介绍

1.1、什么是Bean的生命周期

1.2、Bean生命周期的过程

(1)实例化阶段

(2)依赖注入阶段

(3)初始化阶段

(4)使用阶段

(5)销毁阶段

二、Bean的后置处理器

2.1、如何使用Bean的后置处理器

2.2、后置处理器执行顺序


一、生命周期介绍

1.1、什么是Bean的生命周期

生命周期,指的是:Spring框架中一个Bean对象从实例化到被垃圾回收器回收的这么一个过程,我们通常把这个过程称作:Bean的生命周期。

简单理解,就是一个Bean对象从创建到销毁的这么一个过程,就好比一个人从出生到死亡的这么一个阶段一样。

Spring中Bean的生命周期大致可以分为五个阶段,分别是:实例化、依赖注入、初始化、使用阶段、销毁阶段。下面详细的介绍一下Bean的生命周期过程。

1.2、Bean生命周期的过程

Bean的生命周期大致可以分为下面五个过程,如下所示:

实例化:这个阶段就是Spring加载XML配置文件,然后通过反射机制创建Bean对象的过程。

依赖注入:实例化完成之后,Spring就会调用Bean对应的setXxx()方法、或者构造方法给相应的属性赋值。

初始化:在Bean实例化完成并且依赖注入之后,就会调用Bean的初始化方法,进行一些额外的处理操作,默认初始化方法我们习惯叫做【init()】。

使用阶段:处于这个阶段的Bean对象,就可以真正的被使用啦。

销毁阶段:当某个Bean对象不再被使用时候,此时会首先调用销毁方法(默认销毁方法我们习惯叫做【destory()】),用于释放一些系统资源,然后将Bean对象进行垃圾回收。

(1)实例化阶段

当我们启动Spring工程的时候,此时Spring会加载XML配配置文件,读取里面的bean标签相关配置信息,查找需要被实例化的Bean信息,然后通过Java中的反射机制,创建相应的Bean实例对象。

到此,bean的实例化就结束了。Spring框架中Bean的实例化过程采用了工厂模式,通过统一的Bean工厂创建对应的Bean对象。

(2)依赖注入阶段

通过前几篇文章,我们知道了什么是依赖注入,当我们在XML配置文件里面,通过【property】标签或者【constructor-arg】标签,判断是否需要进行依赖注入。

如果有配置依赖注入,则Spring框架会调用对应的【setXxx()】或者【构造方法】进行属性赋值操作,这个过程我们叫做:依赖注入阶段。

下面看个测试案例:

创建【User】测试类

public class User {
    private Integer uid;
    private String username;

    public User() {
        System.out.println("1、实例化阶段");
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
        System.out.println("2、依赖注入阶段,uid赋值");
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
        System.out.println("2、依赖注入阶段,username赋值");
    }
}

添加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">

    <!-- 配置User类 -->
    <bean id="user" class="com.spring.demo.pojo.User">
        <!-- 属性注入 -->
        <property name="uid" value="1001"/>
        <property name="username" value="CSDN_朱友斌"/>
    </bean>

</beans>

编写测试类

public class Test {
    public static void main(String[] args) {
        // 1、获取 ApplicationContext 容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        // 2、获取 Bean 对象
        User user = context.getBean("user", User.class);
        System.out.println("4、Bean使用阶段");
    }
}

运行测试程序,可以在控制台看到如下输出结果。


为什么没有步骤3呢???因为步骤3是初始化阶段,我们没有显式指定【init()】方法,所以没有输出语句。下面介绍初始化阶段。

(3)初始化阶段

初始化阶段,这个阶段可以额外的对Bean做一些处理,例如:加载一些资源,初始化需要使用的数据等等。一般情况下,Bean的初始化方法是【init()】方法,我们只需要在对应的Bean里面声明一个【init()】方法,然后在XML配置文件里面指定初始化方法名称。

声明初始化【init()】方法

在前面的【User】类里面,新增一个【init()】方法,如下所示:

public void init() {
    System.out.println("3、执行初始化操作");
}


XML配置文件中指定【init-method】属性值


再次运行测试程序,查看控制台结果


以上,就是初始化方法的定义,我们习惯上将初始化方法叫做【init()】,当然你也可以随便起个方法名称,只需要通过【init-method】属性指定。

(4)使用阶段

处于使用阶段的Bean对象,就能够真正的用于处理一些业务逻辑,完成特定的业务需求,这个阶段没有什么可以介绍的。

(5)销毁阶段

当我们的Bean对象不再需要使用后,垃圾回收器就会开始回收Bean对象,再回收之前就会调用Bean对象的销毁方法,一般情况下,销毁方法名称叫做【destory()】,在销毁方法里面,可以用于释放一些资源。通过在XML配置文件里面指定【destory-method】属性声明销毁方法。

定义销毁【destory()】方法

在【User】类里面定义一个【destory()】方法。

public void destory() {
    System.out.println("5、执行销毁操作");
}

XML配置【destory-method】属性


 

测试程序,关闭Spring容器来模拟Bean被销毁的情况

public class Test {
    public static void main(String[] args) {
        // 1、获取 ApplicationContext 容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        // 2、获取 Bean 对象
        User user = context.getBean("user", User.class);
        System.out.println("4、Bean使用阶段");
        // 关闭容器: 这里通过关闭容器来模拟Bean被销毁的操作
        context.close();
    }
}

 运行测试程序,查看控制台输出情况。


以上,就是Bean的生命周期基本的五个过程,其实在Bean的初始化前后我们还可以通过Bean的后置处理器,添加一些额外的功能,下面介绍以下Bean的后置处理器。

二、Bean的后置处理器

2.1、如何使用Bean的后置处理器

后置处理器,是指:我们可以在Bean调用初始化【init()】方法之前或者之后,执行我们指定的一些代码逻辑。

Spring框架中给我们提供了后置处理器接口【BeanPostProcessor】,这个接口中定义两个方法,分别是:

postProcessBeforeInitialization:这个方法在调用【init()】方法之前执行。

postProcessAfterInitialization:这个方法在调用【init()】方法之后执行。

我们可以自定义一个类,然后实现后置处理器接口,重写其中两个方法,然后可以在对应的方法里面进行一些额外的逻辑处理。下面看个案例:

创建【CustomProcess】后置处理器

public class CustomProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在init()之前,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在init()之后,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }
}

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">

    <!-- 配置User类 -->
    <bean id="user" class="com.spring.demo.pojo.User" init-method="init"
          destroy-method="destory">
        <!-- 属性注入 -->
        <property name="uid" value="1001"/>
        <property name="username" value="CSDN_朱友斌"/>
    </bean>

    <!-- 配置后置处理器 -->
    <bean class="com.spring.demo.pojo.CustomProcessor"/>

</beans>

运行测试程序,查看控制台输出结果


以上就是Bean的后置处理器最基础的使用,这个后置处理器是会对所有的Bean的生命周期有效的,也就是说,定义了一个后置处理器,那么所有的Bean在初始化前后都会执行后置处理器。

后置处理器也可以定义多个,这里就有一个问题了,多个后置处理器它们的执行顺序是怎样的呢???下面介绍一下多个后置处理器的执行顺序。

2.2、后置处理器执行顺序


当我们在程序中定义了多个后置处理器时,它们的执行顺序可以分为两种情况,分别如下:

第一种情况:不指定执行顺序,按照配置的先后顺序执行。

第二种情况:手动指定先后执行顺序。

(1)第一种情况:采用默认配置顺序

如果我们有多个后置处理器,那么默认情况下,这些后置处理器是按照在XML配置文件里面的<bean>标签配置的先后顺序执行的。

举个栗子看看默认的执行顺序:

创建【CustomProcessor1】后置处理器

public class CustomProcessor1 implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomProcessor1: 在init()之前,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomProcessor1: 在init()之后,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }
}

创建【CustomProcessor2】后置处理器

public class CustomProcessor2 implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomProcessor2: 在init()之前,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomProcessor2: 在init()之后,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }
}

创建【CustomProcessor3】后置处理器

public class CustomProcessor3 implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomProcessor3: 在init()之前,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomProcessor3: 在init()之后,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }
}

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">

    <!-- 配置User类 -->
    <bean id="user" class="com.spring.demo.pojo.User" init-method="init"
          destroy-method="destory">
        <!-- 属性注入 -->
        <property name="uid" value="1001"/>
        <property name="username" value="CSDN_朱友斌"/>
    </bean>

    <!-- 配置后置处理器 -->
    <bean class="com.spring.demo.pojo.CustomProcessor1"/>
    <bean class="com.spring.demo.pojo.CustomProcessor2"/>
    <bean class="com.spring.demo.pojo.CustomProcessor3"/>

</beans>


上面是按照【1,2,3】的顺序配置的,那么程序执行时候,也是按照【1,2,3】顺序执行的后置处理器,运行测试程序【Test02】,查看控制台输出结果。

这个时候,为了验证默认情况下,后置处理器是按照XML配置顺序执行的,我们可以调整一下XML配置的先后顺序,这里我将顺序调整为【1,3,2】,然后再次运行测试程序,查看结果。


有时候,我们不想按照默认的配置顺序,而是需要自己指定哪个后置处理器先执行,哪个后执行,这个时候就需要手动设置执行顺序,下面介绍如何手动设置先后执行顺序。

(2)第二种情况:手动指定顺序

要实现后置处理器按照指定顺序执行,那么每个后置处理器需要实现【Ordered】接口,然后重写其中的【getOrder()】方法,这个【getOrder()】方法有什么作用呢???

【getOrder()】作用】

这个方法返回一个【int】类型数字,这个数字就是表示后置处理器的先后执行顺序。

默认返回值是【0】。

返回的数字越大,那么优先级越低,也就是执行顺序越低

创建【CustomProcessorOrder01】后置处理器

public class CustomProcessorOrder01 implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomProcessorOrder01: 在init()之前,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomProcessorOrder01: 在init()之后,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }

    @Override
    public int getOrder() {
        return 10; // 这里设置为10
    }
}

创建【CustomProcessorOrder02】后置处理器

public class CustomProcessorOrder02 implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomProcessorOrder02: 在init()之前,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomProcessorOrder02: 在init()之后,执行Bean的后置处理器, 当前bean的名称: " + beanName);
        return bean;
    }

    @Override
    public int getOrder() {
        return 5; // 这里设置为5
    }
}

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">

    <!-- 配置User类 -->
    <bean id="user" class="com.spring.demo.pojo.User" init-method="init"
          destroy-method="destory">
        <!-- 属性注入 -->
        <property name="uid" value="1001"/>
        <property name="username" value="CSDN_朱友斌"/>
    </bean>

    <!-- 配置后置处理器 -->
    <bean class="com.spring.demo.pojo.CustomProcessorOrder01"/>
    <bean class="com.spring.demo.pojo.CustomProcessorOrder02"/>

</beans>

运行测试程序【Test03】,查看控制台输出结果。


从控制台的输出结果,我们可以看到,虽然我们在XML配置文件里面是按照【01,02】两个顺序配置的,但是测试程序输出结果是按照【02,01】执行的,这是因为我们重写了【getOrder()】方法,然后【CustomProcessorOrder01】类中返回是【10】,【CustomProcessorOrder02】类中返回是【5】,返回值越小,越先执行,所以最终结果按照【02,01】执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1063004.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PostgreSQL ash —— pgsentinel插件

一、 插件作用 众所周知&#xff0c;pg是没有像oracle那样的ash视图的&#xff0c;因此要回溯历史问题不太方便。pgsentinel插件会将pg_stat_activity与pg_stat_statements视图内容定期快照&#xff0c;并存入pg_active_session_history和pg_stat_statements_history视图中。 1…

【实操记录】Oracle数据整库同步至Apache Doris

本文是Oracle数据整库同步至Apache Doris实操记录&#xff0c;仅供参考 参考&#xff1a;https://cn.selectdb.com/blog/104 1、Oracle 配置 [rootnode1 oracle]# pwd /u01/app/oracle [rootnode1 oracle]# mkdir recovery_area [rootnode1 oracle]# chown -R oracle:dba re…

WPF中, 如何将控件的触发事件绑定到ViewModel

在DataGrid 等控件中, 有很多这种带闪电符号的触发事件. 如果用传统的事件驱动, 则直接在后台中建立 一个private PropertyChanged(Sender s, EventAgars Args) 即可. 但是如果需要绑定到ViewModel的话? 应该怎么做? 带闪电符号的触发事件 实现viewModel绑定前端触发事件的…

【C++设计模式之原型模式:创建型】分析及示例

简介 原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;它允许通过复制已有对象来生成新的对象&#xff0c;而无需再次使用构造函数。 描述 原型模式通过复制现有对象来创建新的对象&#xff0c;而无需显式地调用构造函数或暴露对象的创建…

JAVA编程题-求矩阵螺旋值

螺旋类 package entity; /*** 打印数组螺旋值类*/ public class Spiral { // 数组行private int row; // 数组列private int col; // 行列数private int size; // 当前行索引private int rowIndex; // 当前列索引private int colIndex; // 行开始索引private int rowStart; //…

大模型部署手记(5)ChatGLM2+Jetson AGX Orin

1.简介&#xff1a; 组织机构&#xff1a;智谱/清华 代码仓&#xff1a;https://github.com/THUDM/ChatGLM2-6B 模型&#xff1a;THUDM/chatglm2-6b 下载&#xff1a;https://huggingface.co/THUDM/chatglm2-6b 镜像下载&#xff1a;https://aliendao.cn/models/THUDM/chat…

Java日期的学习篇

关于日期的学习 目录 关于日期的学习JDK8以前的APIDate Date常用APIDate的API应用 SimpleDateFormatSimpleDateFormat常用API测试 反向格式化(逆操作)测试 训练案例需求(秒杀活动)实现 Calendar需求痛点常见API应用测试 JDK8及以后的API(修改与新增)为啥学习(推荐使用)新增的AP…

ArcGIS Engine:鹰眼图的拓展功能-点击和矩形+坐标状态栏

目录 01 前言 02 鹰眼图的控制功能 03 显示当前鼠标的地理坐标 01 前言 说是拓展&#xff0c;不过是忘记了实验还有附加实验.这里补上. 前文不再赘述,上一节查看&#xff1a;ArcGIS Engine&#xff1a;视图菜单的创建和鹰眼图的实现_炒茄子的博客-CSDN博客 这里加上三个功能…

unity脚本_Vector3 c#

接下来学习 相对世界坐标 首先我们给场景物体一个空物体 修改新建空物体名字为GameObjectFather 修改GameObjectFather坐标 修改GameObject2坐标 然后将GameObjectFahter设置成GameObject2的父物体 我们观察到子物体的坐标改变了但是 运行显示的相对世界坐标this.transform.po…

R语言教程课后习题答案(持续更新中~~)

R语言教程网址如下 https://www.math.pku.edu.cn/teachers/lidf/docs/Rbook/html/_Rbook/index.html 目录 source()函数可以运行保存在一个文本文件中的源程序 R向量下标和子集 数值型向量及其运算 日期功能 R因子类型 source()函数可以运行保存在一个文本文件中的源程序…

学信息系统项目管理师第4版系列18_采购管理

1. 协议 1.1. 合同 1.1.1. 国际合作的项目经理应牢记&#xff0c;无论合同规定如何详尽&#xff0c;文化和当地法律对合同及其可执行性均有影响 1.2. 服务水平协议&#xff08;SLA&#xff09; 1.3. 谅解备忘录 1.4. 协议备忘录&#xff08;MOA&#xff09; 1.5. 订购单 …

十天学完基础数据结构-第八天(哈希表(Hash Table))

哈希表的基本概念 哈希表是一种数据结构&#xff0c;用于存储键值对。它的核心思想是将键通过哈希函数转化为索引&#xff0c;然后将值存储在该索引位置的数据结构中。 哈希函数的作用 哈希函数是哈希表的关键部分。它将输入&#xff08;键&#xff09;映射到哈希表的索引位…

Python常用功能的标准代码

后台运行并保存log 1 2 3 4 5 6 7 8 9 nohup python -u test.py > test.log 2>&1 & #最后的&表示后台运行 #2 输出错误信息到提示符窗口 #1 表示输出信息到提示符窗口, 1前面的&注意添加, 否则还会创建一个名为1的文件 #最后会把日志文件输出到test.log文…

卷积神经网络-池化层和激活层

2.池化层 根据特征图上的局部统计信息进行下采样&#xff0c;在保留有用信息的同时减少特征图的大小。和卷积层不同的是&#xff0c;池化层不包含需要学习的参数。最大池化(max-pooling)在一个局部区域选最大值作为输出&#xff0c;而平均池化(average pooling)计算一个局部区…

卷积神经网络-卷积层

卷积神经网络 卷积神经网络&#xff08;convolutional neural network&#xff0c;CNN&#xff09;是一类包含卷积计算且具有深度结构的前馈神经网络&#xff0c;是深度学习的代表算法之一。卷积神经网络具有表征学习能力&#xff0c;能够按其阶层结构对输入信息进行平移不变分…

H5移动端购物商城系统源码 小型商城全新简洁风格全新UI 支持易支付接口

一款比较简单的 H5 移动端购物商城系统源码&#xff0c;比较适合单品商城、小型商城使用。带有易支付接口。 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/88391704 源码下载2&#xff1a;评论留言或私信留言

【Spring笔记03】Spring依赖注入各种数据类型

这篇文章&#xff0c;详细介绍一下Spring框架中如何注入各种数据类型&#xff0c;包含&#xff1a;注入基本数据类型、数组、集合、Map映射、Property属性、注入空字符串、注入null值、注入特殊字符等内容&#xff0c;以及如何使用命名空间进行依赖注入。 目录 一、注入各种数据…

云原生Kubernetes:简化K8S应用部署工具Helm

目录 一、理论 1.HELM 2.部署HELM2 3.部署HELM3 二、实验 1.部署 HELM2 2.部署HELM3 三、问题 1.api版本过期 2.helm初始化报错 3.pod状态为ImagePullBackOff 4.helm 命令显示 no repositories to show 的错误 5.Helm安装报错 6.git命令报错 7.CentOS 7 下git c…

互联网Java工程师面试题·Elasticsearch 篇·第一弹

目录 1、elasticsearch 了解多少&#xff0c;说说你们公司 es 的集群架构&#xff0c;索引数据大小&#xff0c;分片有多少&#xff0c;以及一些调优手段 。 1.1 设计阶段调优 1.2 写入调优 1.3 查询调优 1.4 其他调优 2、elasticsearch 的倒排索引是什么 3、elastic…

ToDoList使用自定义事件传值

MyTop与MyFooter与App之间传递数据涉及到的就是子给父传递数据&#xff0c;MyList和MyItem与App涉及到爷孙传递数据。 之前的MyTop是使用props接收App传值&#xff0c;然后再在methods里面调用&#xff0c;现在使用自定义事件来处理子组件和父组件之间传递数据。 图是之前的…