面试官必问--谈谈Spring Bean对象的生命周期吧

news2024/11/27 1:38:43

现在是时候讨论Spring Bean从产生到销毁整个过程的细节了,也就是Spring Bean的生命周期。在这里文哥先温馨提示:Spring Bean的生命周期是面试高频点之一,希望大家好好掌握哦~

一. Spring Bean生命周期的概述

如果没有Spring的环境,Java Bean的生命周期非常简单,通过new关键字创建的对象就可以被使用,一旦这个对象不再被使用了(JVM中通过可达性搜索算法判断对象是否可用),这个对象就会被判定为垃圾对象,然后被垃圾回收器回收。

但是在Spring中,Bean的生命周期就不是这么简单的了。由于Spring对Bean管理灵活度非常高,这就导致Spring Bean的生命周期非常复杂。接下来,文哥带领大家一探Spring Bean生命周期的细节。为了完整地展示Spring的生命周期,文哥用一幅图来描述Spring Bean生命周期的整个过程。

我们发现整个生命周期异常复杂,别慌,文哥这就给大家慢慢地分析,跟我来。

二. 详解Spring Bean的生命周期

1. BeanNameAware

这是一个接口,在Spring源码中是这样描述的。如果Spring中的Bean实现了这个接口的话,Spring的Bean的id将会传入给setBeanName的形参里面。

现在我们验证一下是不是如我们上面定义的那样,接下来我们通过几个步骤来进行分析。

1.1 第一步:定义一个类,该类必须实现BeanNameAware接口

public class User implements BeanNameAware 
{    
  public void setBeanName(String name) 
    {        
      System.out.println("bean的名称是:" + name);    
    } 
}

1.2 第二步:在Spring的配置文件中管理Bean

<?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 id="user" class="com.qf.bean.User"></bean>
</beans>

1.3 第三步:编写测试类

public class TestUser {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
}

1.4 第四步:打断点测试

我们在User的setBeanName上打上断点,查看一下效果:

以上结果就验证了刚刚说的,这个方法里面会接收Bean的id的值。

2. BeanClassLoaderAware

在源码中,会将其描述成一个接口。

这个接口里面定义了setBeanClassLoader方法。这个方法就是设置Bean加载器的方法,方法的形式参数传递的是一个类加载器对象。

3. BeanFactoryAware

Spring也是将其描述成了一个接口。

如果一个Bean实现了这个接口,可以获取这个Bean的Bean工厂。

4. EnvironmentAware

如果一个Bean实现了这个接口,就可以获取当前Bean对应的运行环境。

如果我们想获取Bean对应的环境信息,我们可以这么做:

public class User implements  EnvironmentAware{
    public void setEnvironment(Environment environment) {
        System.out.println(environment);
    }
}

5. ResourceLoaderAware

这也是一个接口。

如果一个Bean实现了这个接口我们可以获得一个资源加载器,用来去加载Bean所需要用到的资源信息。

6. ApplicationEventPublisherAware

这个接口是用来做事件发布用的。Spring设计这个组件主要是用来组件解耦使用的,如果一个Bean所属的类实现了这个接口,就可以获取一个事件发布器。

7. MessageSourceAware

Spring同样将其设计成了一个接口,我们可以通过这个接口去获取Bean相关的国际化资源信息。

8. ApplicationContextAware

当一个Bean所属的类实现了这个接口之后,这个类就可以方便地获得ApplicationContext对象(Spring上下文),Spring发现某个Bean实现了ApplicationContextAware接口,Spring容器会在创建该Bean之后,自动调用该Bean的setApplicationContext(参数)方法,调用该方法时,会将容器本身ApplicationContext对象作为参数传递给该方法。

我们在Bean所属的类上面实现这个接口:

public class User implements ApplicationContextAware {
    private ApplicationContext context;

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

}

我们打断点验证我们的猜想:

我们看看这个context是什么:

这不就是我们上面说的,将ApplicationContext实例对象传入进来了吗?

9. ServletContextAware

在上面我们获取了Spring的Bean工厂实例对象,接下来Spring框架会去判断当前容器是否是一个Web类型的容器实例,如何判断?就需要调用ServletContextAware中的setServletContext方法,获取一个ServletContext容器。那到底是如何获取的呢?

private ServletContext servletContext;
@Override
public void setServletContext(ServletContext servletContext) {
    this.servletContext = servletContext;
}

10. BeanPostProcessor

该接口我们也叫Bean的后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显式调用初始化方法的前后添加我们自己自定义的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。接口的源码如下

public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

这两个方法是什么意思呢,给大家详细介绍:

  • postProcessBeforeInitialization:Bean实例化、依赖注入完毕之后,在调用Bean初始化之前完成一些定制的初始化任务

  • postProcessAfterInitialization:Bean实例化、依赖注入、初始化完毕时执行

现在,我们就自定义一个Bean的后置处理器,有以下几个实现步骤。

10.1 第一步:创建一个类,实现BeanPostProcessor接口


public class CustomizeBeanProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行before方法,bean是:" + bean + "bean的名称是:" + beanName);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行after方法,bean是:" + bean + "bean的名称是:" + beanName);
        return bean;
    }
}

文哥在这里特别提示:这两个方法的返回值千万不能返回为null。如果返回null那么在后续初始化方法因为通过getBean()方法获取不到Bean实例对象会报空指针异常,因为后置处理器从Spring IoC容器中取出Bean实例对象没有再次放回IoC容器中。

10.2 第二步:定义一个Pojo类

我们定义一个Student类,在里面定义一个init方法。

public class Student {
    
    private Integer id;
    private String name;
    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    //定义一个初始化bean的时候需要执行的方法
    public void init(){
        System.out.println("初始化方法init执行了.....");
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

10.3 第三步:在Spring的配置文件中配置pojo和Bean的后置处理器

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

    <!--管理student-->
    <bean id="student" class="com.qf.bean.Student" init-method="init">
        <property name="id" value="10010"></property>
        <property name="name" value="eric"></property>
        <property name="age" value="12"></property>
    </bean>
    
    <!--管理bean的后置处理器-->
    <bean class="com.qf.processor.CustomizeBeanProcessor"></bean>
</beans

10.4 第四步:测试

我们定义一个测试类,初始化一个IOC容器,然后从容器中获取Bean,然后查看控制台效果。

public class TestStudent {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student);
    }
}

我们查看控制台输出信息:

通过验证,我们终于知道了Bean的后置处理器的作用了。

11. InitializingBean

该接口只有这一个接口。这个方法将在所有的属性被初始化后调用,但是会在 init 前调用。这个接口我们一般在工作中用于工厂+策略模式优化过多的if else代码块。

现在文哥给大家演示一下,这个接口的简单用法:

11.1 第一步:在pojo类上实现这个接口。

11.2 第二步:运行测试代码,查看控制台效果

到这里我们基本上给大家把Spring Bean生命周期中所涉及的主要接口给大家讲解清楚了。接下来文哥再给大家用代码演示Spring Bean的生命周期。

三. 验证Spring Bean的生命周期

1. 定义一个POJO类

定义一个POJO类,我们分别实现上面刚刚给大家介绍的生命周期中的接口。

public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware,InitializingBean, DisposableBean {

    private Integer id;
    private String name;
    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    //定义一个初始化bean的时候需要执行的方法
    public void init(){
        System.out.println("初始化方法init执行了.....");
    }

    //定义销毁bean的时候需要指定的方法
    public void preDestroy(){
        System.out.println("销毁bean的方法preDestroy执行了....");
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("Student的setBeanFactory方法执行了.....");
    }

    public void setBeanName(String beanName) {
        System.out.println("Student的bean的名称是:" + beanName);
    }

    public void destroy() throws Exception {
        System.out.println("destroy方法执行了....");
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet被调用了....");
    }
}

2. 定义Bean的后置处理器

这个后置处理器,在上面文哥给大家介绍过,我们需要实现里面的两个方法,注意方法的返回值一定不能为null。


public class CustomizeBeanProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行before方法,bean是:" + bean + "bean的名称是:" + beanName);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行after方法,bean是:" + bean + "bean的名称是:" + beanName);
        return bean;
    }
}

3. 定义Spring的配置文件

定义Spring的配置文件,管理我们的POJO类和后置处理器类。

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

    <!--管理student-->
    <bean id="student" class="com.qf.bean.Student" init-method="init" destroy-method="preDestroy">
        <property name="id" value="10010"></property>
        <property name="name" value="eric"></property>
        <property name="age" value="12"></property>
    </bean>

    <!--管理bean的后置处理器-->
    <bean class="com.qf.processor.CustomizeBeanProcessor"></bean>
</beans>

4. 定义测试类,查看控制台输出效果


public class TestStudent {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student);
        //关闭容器,销毁Bean
        ((ClassPathXmlApplicationContext) context).close();
    }
}

我们此时来查看一下控制台的效果,如下图所示:

通过这篇文章,给大家非常详细地介绍了Spring中Bean的生命周期,可以说本文是从源码角度,深入浅出地剖析了Spring中Bean的生命周期整个执行流程。虽然本文的讲解步骤比较繁琐,但如果大家在面试中能够回答的这么深入,相信大家一定可以让面试官刮目相看!

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

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

相关文章

张力控制之开环模式

张力控制的相关知识也可以参看专栏的其它文章,链接如下: 张力闭环控制之传感器篇(精密调节气阀应用)_RXXW_Dor的博客-CSDN博客跳舞轮对应张力调节范围,我们可以通过改变气缸的气压方式间接改变,张力跳舞轮在收放卷闭环控制上的详细应用,可以参看下面的文章链接,这里我…

人工智能实验一:利用遗传算法求解 TSP(旅行商)问题

1.任务描述 本关任务&#xff1a;利用遗传算法求解 TSP 问题。 2.相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1. 遗传算法&#xff1b;2. TSP问题。 遗传算法 一个后继状态由两个父状态决定&#xff0c;以k个随机产生的状态开始&#xff08;population&…

Kaggle赛题解析:Diffusion Prompt生成

文章目录一、比赛信息二、比赛背景三、比赛任务四、评价指标五、数据描述六、解题思路一、比赛信息 比赛名称&#xff1a;Stable Diffusion - Image to Prompts 推断生成高度详细、清晰的焦点、插图、宏伟、史诗般的 3d 渲染图像的prompt 比赛链接&#xff1a;https://www.k…

python----获取一部小说

1、需求说明 获取一部小说的标题内容&#xff0c;以txt文档形式保存 2、项目说明 3、代码 # 怎么发送请求 # pip install requests import requests# pip install lxml->从标签里提起文字 #from lxml import etree from lxml import html etreehtml.etree # 发送给谁 url…

Android---系统启动流程

目录 Android 系统启动流程 init 进程分析 init.rc 解析 Zygote 概叙 Zygote 触发过程 Zygote 启动过程 什么时Runtime&#xff1f; System Server 启动流程 Fork 函数 总结 面试题 Android 是 google 公司开发的一款基于 Linux 的开源操作系统。 Android 系统启动…

Web3中文|一波未平一波又起:Silvergate将走向何处

Silvergate Capital&#xff08;SI&#xff09;这一加密公司曾经的重要银行合作伙伴&#xff0c;现在正处于崩溃的边缘。这家总部位于加州拉荷亚的公司上周五晚上表示&#xff0c;其暂停了Silvergate交易所网络&#xff08;SEN&#xff1a;Silvergate Exchange Network&#xf…

Foxit PDF SDK ActiveX 5.9.7 Crack

Foxit PDF SDK ActiveX对于刚接触PDF或不愿投入过多精力学习PDF技术的产品管理者及开发者来说&#xff0c;Foxit PDF SDK ActiveX无疑是理想的选择。破解版它拥有操作简单的特性&#xff0c;提供可支持定制的可视化编程组件&#xff0c;开发者通过简单的拖放动作&#xff0c;就…

扬帆配资|建筑业景气度持续回升,多只概念股业绩有望增长

新式城镇化概念股遭到商场重视。 今天早盘&#xff0c;新式城镇化概念股冲高&#xff0c;恒锋信息、ST花王涨停。蕾奥规划、筑博规划一度冲高至15%&#xff0c;冠龙节能、杭州园林、美晨生态跟涨。 国家出台一系列城镇化相关方针 城镇化&#xff0c;是人口向城镇会集的进程。…

【Kubernetes】第二十三篇 - 布署 nodejs 后端项目(上)

一&#xff0c;前言 上一篇&#xff0c;介绍了 MySQL 服务的部署&#xff1b; 本篇&#xff0c;介绍 nodejs 后端项目的布署&#xff08;将后端项目构建成为 docker 镜像&#xff0c;并推送至镜像仓库&#xff09;&#xff1b; 二&#xff0c;准备项目 创建后端项目&#xf…

8、LSM树

一、前言 最近在调研NoSQL数据库&#xff0c;发现RocksDB、LevelDB、HBase以及Prometheus等&#xff0c;其底层的存储引擎都是基于LSM树&#xff0c;于是决定花时间彻底吃透LSM树这一数据结构。 不幸的是&#xff0c;在查阅资料学习的过程中&#xff0c;发现网上各种文章汗牛…

浅谈对Promise的理解以及在工作中的应用

浅谈对Promise的理解以及在工作中的应用Promise的概念背景知识JavaScript的同步和异步JavaScript事件循环回调函数进行异步操作解决方案&#xff1a;PromisePromise 在工作中的运用创建PromisePromise封装AJAXPromise链式操作Promise.all()Promise.race()async和await总结Promi…

轻松转换文档:antennahouse/Office Server Document Converter

关于 Office Server 文档转换器 (OSDC)破解版 无需 Microsoft Office 或 Adob​​e 软件即可快速准确地转换文档。 Office Server 文档转换器 (OSDC) 会将您在 Microsoft Office&#xff08;Word、Excel、PowerPoint&#xff09;中创建的重要文档转换为高质量的 PDF 或图像格式…

国内32位MCU在电机控制上的应用方案

电机&#xff08;Electric machinery&#xff0c;俗称“马达”&#xff09;是依据电磁感应定律&#xff0c;实现电能转换或传递的一种电磁装置&#xff0c;其主要作用是产生驱动转矩&#xff0c;为用电器或各类机械提供动力。电机作为工业世界的动力之源&#xff0c;几乎用于所…

ThinkPHP 6.1 模板篇之布局与继承

本文主要讲述ThinkPHP 6.1版本模板几种布局的方法和如何实现继承&#xff0c; 可以与《ThinkPHP 6.1 模板篇之文件加载》结合来看。 模板布局 布局方式有两种可以实现。 布局方法1 开启配置 默认情况下&#xff0c;不支持模版布局功能&#xff0c;需要在配置文件中开启&…

如何快速通过PMP考试?

我建议准备的最短时间至少一个月&#xff0c;我用了一个半月&#xff0c;我每天集中精力备考大约4个小时&#xff0c;大家可以根据自己的专注力的长短去调节每天的备考时间。 准备5月的&#xff0c;还没备考的&#xff0c;现在开始也来得及。5月没有报名的可以准备8月的&#…

【Linux系统编程】05:多进程

多进程 OVERVIEW多进程一、进程创建1.创建1个子进程2.创建多个子进程二、进程控制1.进程结束2.进程等待3.子进程操作14.子进程操作2三、进程体系1.守护进程2.进程调度程序&#xff1a;一种已经编译好的、存在磁盘中的二进制文件&#xff08;脚本为普通文件&#xff09;。进程&a…

超图iServer扩展开发记录Restlet 3

HTTP 请求在达到 REST 应用对象&#xff0c;交给资源实现类处理的时候&#xff0c;先要解析 HTTP 请求中的参数&#xff0c;然后才会进入业务逻辑进行处理。参数解析的工作由参数解析器&#xff08;Decoder&#xff09;进行&#xff0c;即可以实现将请求参数转换为 Java 对象。…

qt tcp通讯

TCP 协议&#xff08;Transmission Control Protocol&#xff09;全称是传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。tcp服务端使用QTcpServer、QTcpSocket。tcp客户端使用QTcpSocket1.在工程文件(工程文件.pro)中的第一行添加network 如QT core gui …

WeSpeaker支持C++部署链路

WeSpeaker正式更新C部署链路&#xff0c;推理引擎使用OnnxRuntime&#xff0c;支持从语音中提取Speaker Embedding信息&#xff0c;代码详见WeSpeaker/runtime[1]。 Libtorch和onnx的选择? Speaker Embedding提取任务流程简单&#xff0c;并且声纹模型&#xff08;如ResNet\E…

前端js学习

1. js入门 1.1 js是弱类型语言 1.2 js使用方式 1.2.1 在script中写 1.2.2 引入js文件 1.2.3 优先级 1.3 js查错方式 1.4 js变量定义 1.4 js数据类型 数据类型英文表示示例数值类型number1.1 1字符串类型string‘a’ ‘abc’ “abc”对象类型object布尔类型booleannumber函数…