Java面试宝典:说下Spring Bean的生命周期?
Java面试宝典专栏范围:JAVA基础,面向对象编程(OOP),异常处理,集合框架,Java I/O,多线程编程,设计模式,网络编程,框架和工具等全方位面试题详解
每日更新Java面试宝典专栏:Java面试宝典
感兴趣的可以先收藏起来,大家在遇到JAVA面试题等相关问题都可以给我留言咨询,希望帮助更多的人

回答重点
实例化: Spring 容器根据配置文件或注解实例化 Bean 对象。
属性注入: Spring 将依赖(通过构造器、setter 方法或字段注入)注入到 Bean 实例中。
初始化前的扩展机制: 若 Bean 实现了 BeanNameAware 等 aware 接口,则执行 aware 注入。
初始化前(BeanPostProcessor): 在 Bean 初始化之前,可通过 BeanPostProcessor 接口对 Bean 进行额外处理。
初始化: 调用 InitializingBean 接口的 afterPropertiesSet () 方法或通过 init - method 属性指定的初始化方法。
初始化后(BeanPostProcessor): 在 Bean 初始化后,可通过 BeanPostProcessor 进一步处理。
使用 Bean: Bean 初始化完成后,可被容器中的其他 Bean 使用。
销毁: 当容器关闭时,Spring 调用 DisposableBean 接口的 destroy () 方法或通过 destroy - method 属性指定的销毁方法。
详细解释
实例化
Spring 容器在启动过程中,会扫描指定的配置文件(如 XML 配置文件)或带有特定注解(如 @Component、@Service、@Repository、@Controller 等)的类。对于 XML 配置,会根据 <bean> 标签的定义来创建 Bean 实例;对于注解方式,容器会利用反射机制来实例化对应的类。例如,当容器扫描到一个标注了 @Service 的类时,会创建该类的对象,这个对象就是一个 Bean 实例。
属性注入
- 构造器注入: 通过在目标类中定义带有参数的构造函数,Spring 会根据配置或注解找到对应的依赖对象,然后将这些依赖对象作为参数传递给构造函数来创建 Bean 实例。比如一个
UserService类依赖UserRepository,可以在UserService的构造函数中接收UserRepository作为参数,Spring 会自动将合适的UserRepository实例注入进来。 - setter 方法注入: 在目标类中为需要注入的属性提供对应的 setter 方法,Spring 会在 Bean 实例化之后,调用 setter 方法将依赖对象注入到属性中。例如,
UserService类有一个userRepository属性,提供setUserRepository方法,Spring 会调用该方法将UserRepository实例设置进去。 - 字段注入: 直接在类的字段上使用
@Autowired等注解,Spring 会自动将匹配的依赖对象注入到该字段中。不过字段注入可能会导致一些测试和依赖管理上的问题,因为它使得依赖关系不够明确。
初始化前的扩展机制
当 Bean 实现了 BeanNameAware 接口时,Spring 容器会在 Bean 实例化之后,在其他初始化操作之前,将当前 Bean 在配置文件或注解中定义的名称注入到 Bean 中。通过实现该接口的 setBeanName 方法,Bean 可以获取自身的名称。类似的,还有 BeanFactoryAware 接口,实现它可以让 Bean 获取到当前的 BeanFactory 实例,从而可以在 Bean 内部对容器进行一些操作,比如获取其他 Bean 等。
初始化前(BeanPostProcessor)
BeanPostProcessor 是 Spring 提供的一个强大的扩展接口。开发者可以自定义实现该接口的类,在其中实现 postProcessBeforeInitialization 方法。当容器创建每个 Bean 实例并完成属性注入后,在调用 Bean 的初始化方法之前,会先调用所有注册的 BeanPostProcessor 的 postProcessBeforeInitialization 方法,开发者可以在这个方法中对 Bean 进行一些额外的处理,比如修改 Bean 的属性值、动态代理增强等操作。
初始化
- 实现 InitializingBean 接口: 当 Bean 实现了
InitializingBean接口时,在 Bean 的属性注入完成之后,Spring 会调用该接口的afterPropertiesSet方法。可以在这个方法中编写一些初始化逻辑,比如对一些资源的初始化操作、建立数据库连接等。 - 通过 init - method 属性: 在 XML 配置中,可以为
<bean>标签指定init - method属性,其值为 Bean 类中定义的一个无参方法名。Spring 会在属性注入完成后,调用这个指定的方法进行初始化操作。同样,在使用注解配置时,也可以通过@Bean注解的initMethod属性来指定类似的初始化方法。
初始化后(BeanPostProcessor)
BeanPostProcessor 的 postProcessAfterInitialization 方法会在 Bean 的初始化方法调用完成之后被调用。在这里可以对已经初始化好的 Bean 进行进一步的处理,比如再次进行动态代理增强、添加一些额外的功能逻辑等。一些框架整合时,会利用这个时机来对 Bean 进行增强以实现特定的功能,比如 AOP 代理的创建等。
使用 Bean
当一个 Bean 完成了上述所有的生命周期步骤,就可以被容器中的其他 Bean 依赖和使用了。其他 Bean 可以通过属性注入等方式获取到这个 Bean 的实例,然后调用其提供的方法来完成业务逻辑。例如,OrderService 依赖 UserService,UserService 已经完成初始化,OrderService 可以通过构造器注入获取到 UserService 实例,进而调用 UserService 中的方法来处理订单相关业务中涉及用户的操作。
销毁
- 实现 DisposableBean 接口: 当 Bean 实现了
DisposableBean接口时,在 Spring 容器关闭时,会调用该接口的destroy方法。可以在这个方法中编写一些资源释放的逻辑,比如关闭数据库连接、释放文件句柄等。 - 通过 destroy - method 属性: 类似于初始化方法,在 XML 配置中可以为
<bean>标签指定destroy - method属性,其值为 Bean 类中定义的一个无参方法名。Spring 容器关闭时,会调用这个指定的方法来进行资源清理等销毁操作。在注解配置中,也可以通过@Bean注解的destroyMethod属性来指定销毁方法。
Bean 的作用域(Scope)与生命周期的关系
Spring Bean 的生命周期还与其作用域密切相关:
- Singleton(单例): 默认作用域,Bean 的生命周期与 Spring 容器的生命周期一致。在容器启动时创建,在容器关闭时销毁。
- Prototype(原型): 每次请求时创建一个新的 Bean 实例,容器只负责创建,不管理其生命周期(不调用销毁方法)。
- Request、Session、Application、WebSocket: 这些作用域用于 Web 应用中,Bean 的生命周期分别与 HTTP 请求、会话、应用或 WebSocket 的生命周期一致。
常见的生命周期场景有哪些?
数据库连接初始化与关闭
在一个 Web 应用中,经常需要与数据库进行交互。我们可以创建一个DatabaseConnection的 Bean。
- 实例化与属性注入: Spring 容器启动时,根据配置实例化
DatabaseConnection对象,并注入数据库连接所需的配置信息,比如数据库地址、用户名、密码等。 - 初始化: 可以实现
InitializingBean接口,在afterPropertiesSet方法中编写代码来建立实际的数据库连接,比如使用 JDBC 驱动来获取一个数据库连接对象。 - 使用: 其他业务逻辑相关的 Bean,比如
UserRepository,可以通过属性注入获取DatabaseConnection实例,然后利用这个连接来执行 SQL 语句,查询、插入、更新或删除数据。 - 销毁: 当应用关闭时,实现
DisposableBean接口,在destroy方法中关闭数据库连接,释放相关资源,避免资源泄漏。
日志记录器配置
很多应用都需要记录日志来跟踪程序运行情况。我们可以创建一个LoggerBean。
- 实例化与属性注入: Spring 容器启动时创建
LoggerBean实例,并注入日志相关的配置,例如日志级别、日志文件路径等。 - 初始化: 在
LoggerBean中可以实现InitializingBean接口或者通过init - method指定一个初始化方法。在这个方法里,根据注入的配置信息来初始化日志记录器,比如设置日志输出格式,加载日志配置文件等。 - 使用: 应用中的其他 Bean,比如
OrderService,可以注入LoggerBean,在业务方法中调用LoggerBean的方法来记录日志,例如在处理订单时记录订单创建时间、订单状态变更等信息。 - 销毁: 应用关闭时,
LoggerBean的销毁方法(如果实现了DisposableBean接口或者配置了destroy - method)可以执行一些清理工作,比如关闭日志文件等。
消息队列生产者初始化
在分布式系统中,经常会使用消息队列来进行异步通信。假设有一个MessageProducer的 Bean 用于向消息队列发送消息。
- 实例化与属性注入: Spring 容器启动时实例化
MessageProducer,并注入消息队列的连接信息,如消息队列服务器地址、端口等,以及一些生产者相关的配置,比如消息持久化策略等。 - 初始化: 可以通过实现
InitializingBean接口,在afterPropertiesSet方法中建立与消息队列服务器的连接,创建消息生产者对象,并进行一些必要的配置初始化。 - 使用: 业务 Bean,比如
ProductService,当有新商品发布等事件发生时,可以注入MessageProducer,调用其方法将相关消息发送到消息队列中,以便其他系统或组件进行处理。 - 销毁: 当应用关闭时,
MessageProducer的销毁方法可以关闭与消息队列的连接,释放资源,确保系统正常退出。
在Spring中,如何自定义一个Bean的生命周期?
下面将通过多种方式来展示在 Spring 中自定义 Bean 生命周期的具体 Java 代码示例,包含使用接口、注解以及 XML 配置等方式。
1. 实现 InitializingBean 和 DisposableBean 接口
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;// 使用 @Component 注解将该类注册为 Spring Bean
@Component
public class MyBeanUsingInterfaces implements InitializingBean, DisposableBean {// 实现 InitializingBean 接口的 afterPropertiesSet 方法,用于初始化操作@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("MyBeanUsingInterfaces: Initializing...");}// 实现 DisposableBean 接口的 destroy 方法,用于销毁操作@Overridepublic void destroy() throws Exception {System.out.println("MyBeanUsingInterfaces: Destroying...");}
}
2. 使用 @PostConstruct 和 @PreDestroy 注解
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;// 使用 @Component 注解将该类注册为 Spring Bean
@Component
public class MyBeanUsingAnnotations {// 使用 @PostConstruct 注解,在 Bean 属性注入完成后执行初始化操作@PostConstructpublic void init() {System.out.println("MyBeanUsingAnnotations: Initializing...");}// 使用 @PreDestroy 注解,在 Spring 容器关闭时执行销毁操作@PreDestroypublic void destroy() {System.out.println("MyBeanUsingAnnotations: Destroying...");}
}
3. 在 XML 配置文件中指定 init-method 和 destroy-method 属性
首先创建一个 Java 类:
public class MyBeanUsingXml {// 初始化方法public void init() {System.out.println("MyBeanUsingXml: Initializing...");}// 销毁方法public void destroy() {System.out.println("MyBeanUsingXml: Destroying...");}
}
然后在 applicationContext.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 定义 MyBeanUsingXml Bean,并指定初始化和销毁方法 --><bean id="myBeanUsingXml" class="com.example.MyBeanUsingXml"init-method="init" destroy-method="destroy"/>
</beans>
4. 实现 BeanPostProcessor 接口
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;// 使用 @Component 注解将该类注册为 Spring Bean
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {// 在 Bean 初始化前执行的方法@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("Before Initialization: " + beanName);return bean;}// 在 Bean 初始化后执行的方法@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("After Initialization: " + beanName);return bean;}
}
测试代码
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {// 对于使用注解的方式AnnotationConfigApplicationContext annotationContext = new AnnotationConfigApplicationContext();annotationContext.scan("com.example");annotationContext.refresh();// 获取并使用 BeanMyBeanUsingInterfaces myBeanUsingInterfaces = annotationContext.getBean(MyBeanUsingInterfaces.class);MyBeanUsingAnnotations myBeanUsingAnnotations = annotationContext.getBean(MyBeanUsingAnnotations.class);// 关闭容器,触发销毁方法annotationContext.close();// 对于使用 XML 配置的方式ClassPathXmlApplicationContext xmlContext = new ClassPathXmlApplicationContext("applicationContext.xml");MyBeanUsingXml myBeanUsingXml = xmlContext.getBean("myBeanUsingXml", MyBeanUsingXml.class);xmlContext.close();}
}
代码解释
- 实现接口方式: 通过实现
InitializingBean和DisposableBean接口,Spring 会自动调用相应的方法来完成初始化和销毁操作。 - 注解方式: 使用
@PostConstruct和@PreDestroy注解,让代码更简洁,Spring 会在合适的时机调用被注解的方法。 - XML 配置方式: 在 XML 中指定
init - method和destroy - method,Spring 会根据配置调用对应的方法。 - BeanPostProcessor 方式: 可以在所有 Bean 的初始化前后进行统一的处理,例如日志记录、属性修改等。
相关文章:
Java面试宝典:说下Spring Bean的生命周期?
Java面试宝典专栏范围:JAVA基础,面向对象编程(OOP),异常处理,集合框架,Java I/O,多线程编程,设计模式,网络编程,框架和工具等全方位面试题详解 每…...
early bird inject
基本原理 本质是利用windows系统的apc机制,以及涉及到windows进程启动的流程. 因为线程初始化阶段LdrInitializeThunk函数会调用NtTestAlert函数,这个函数执行后,所有apc队列中的例程都会执行.因此我们在主线程初始化之前向主线程的apc队列中加入恶意代码即可实现…...
Spring Boot 的约定优于配置:简化开发的艺术
# Spring Boot 的约定优于配置:简化开发的艺术 在现代软件开发中,Spring Boot 凭借其“约定优于配置”(Convention Over Configuration,简称 CoC)的理念,极大地简化了 Spring 应用的开发流程。本文将深入探…...
WSL Ubuntu 安装 CUDA 教程
WSL Ubuntu 安装 CUDA 教程 1. 概述2. 准备工作3. 删除旧的 GPG 密钥4. 安装 CUDA Toolkit4.1 使用 WSL-Ubuntu 包安装(推荐) 5. 设置环境变量6. 注意事项7. 参考链接8. 总结 1. 概述 随着 WSL 2 的推出,Windows 用户现在可以在 Windows 子系…...
docker运行perplexica
序 本文主要研究一下如何用docker运行perplexica 步骤 git clone git clone https://github.com/ItzCrazyKns/Perplexica.gitapp.dockerfile FROM docker.1ms.run/node:20.18.0-alpineARG NEXT_PUBLIC_WS_URLws://127.0.0.1:3001 ARG NEXT_PUBLIC_API_URLhttp://127.0.0.1…...
15、Python面试题解析:列表推导式-条件推导与嵌套推导
1. 列表推导式简介 列表推导式(List Comprehension)是 Python 中一种简洁的创建列表的方式。它允许我们通过一行代码生成列表,通常比传统的 for 循环更简洁、更易读。 基本语法 [表达式 for 元素 in 可迭代对象]表达式:对元素的…...
uvm错误记录4
如下所示,奇怪的是penable莫名其妙的出X。可问题,我发送激励了。 仔细定位发现,39行用的是vif中的penable, 问题是都是赋值,却出现同时赋值多次,这是因为nonblocking和blocking同时触发导致的,因此…...
正则表达式(Regular expresssion)
正则表达式 匹配单次 . :匹配任意一个字符 [ ] :匹配[ ]里举例的任意一个字符 /d :匹配数字0-9 /D :匹配非数字 /s :匹配空白或tab建 /S :匹配非空白 /w :…...
React 中级教程
1. useState 与 setState 深入理解 import React, { useState } from react;const Counter = () => {const [count, setCount] = useState(0);const increment = () => {setCount(count + 1); // setState 会异步更新};return (<div><p>Count: {count}</…...
3dtiles——Cesium ion for Autodesk Revit Add-In插件
一、说明: Cesium已经支持3dtiles的模型格式转换; 可以从Cesium官方Aesset中上传gltf等格式文件转换为3dtiles; 也可以下载插件(例如revit-cesium插件)转换并自动上传到Cesium官方Aseet中。 Revit转3dtiles插件使用…...
高级 Conda 使用:环境导出、共享与优化
1. 引言 在 Conda 的基础包管理功能中,我们了解了如何安装、更新和卸载包。但对于开发者来说,如何更好地管理环境、导出环境配置、共享环境,以及如何优化 Conda 的使用效率,才是提高工作效率的关键。本篇博客将进一步深入 Conda …...
函数perror 和全局变量errno
#include <stdio.h> #include <errno.h> #include <fcntl.h>int main() {int fd open("nonexistent_file.txt", O_RDONLY);if (fd -1) {perror("Failed to open file");}return 0; }控制台有如下输出 Failed to open file: No such f…...
微信小程序的制作
制作微信小程序的过程大致可以分为几个步骤:从环境搭建、项目创建,到开发、调试和发布。下面我会为你简要介绍每个步骤。 1. 准备工作 在开始开发微信小程序之前,你需要确保你已经完成了以下几个步骤: 注册微信小程序账号&…...
QT 异步编程之多线程
一、概述 1、在进行桌面应用程序开发的时候,假设应用程序在某些情况下需要处理比较复制的逻辑,如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作。这种情况下就需要使用多线程,其中一个线程处理窗口事…...
人工智能之数学基础:线性子空间
本文重点 在前面的课程中,我们学习了线性空间,本文我们我们在此基础上学习线性子空间。在应用中,线性子空间的概念被广泛应用于信号处理、机器学习、图像处理等领域。 子空间的性质 子空间是线性空间的一部分,它需要满足下面的性质: 设V是数域F上的线性空间,W是V的一个…...
Proxmox 更新软件包数据库(TASK ERROR: command ‘apt-get update‘ failed: exit code 100)
1、连接自己报错的物理机Shell,编辑文件 vi /etc/apt/sources.list.d/pve-enterprise.list 2、注释文件的第一行在开头加上# 按I进入编辑模式后 开头添加# 然后shift: 输入wq或者wq!进行保存 3、注释后执行两个命令apt-get update 和 apt…...
Python--常见库与函数
二、Python常见库与函数 2.1 OS库 常用功能:文件/目录操作、路径管理、环境变量。 import os # 路径操作 abs_path os.path.abspath("data.txt") exists os.path.exists(abs_path) # 目录操作 os.mkdir("logs") # 创建目录(需处…...
算法——数学建模的十大常用算法
数学建模的十大常用算法在数学建模竞赛和实际问题解决中起着至关重要的作用。以下是这些算法的具体信息、应用场景以及部分算法的C语言代码示例(由于篇幅限制,这里只给出部分算法的简要代码或思路,实际应用中可能需要根据具体问题进行调整和扩…...
JVM——垃圾回收算法
目录 垃圾回收算法 评价标准: 标记-清除算法: 复制算法: 标记-整理算法: 分代GC: arthas查看分代之后的内存情况: 垃圾回收算法 java是如何实现垃圾回收的呢?简单来说,垃圾回…...
游戏APP如何通过精准广告策略实现广告变现收益增长?
不同类型的游戏APP合作AdSet聚合广告平台,量身定制广告变现方案,以提升变现收益的表现。#app广告变现 1、智能选择广告形式 基于用户的行为习惯,推荐优化广告格式,包括插屏广告、激励视频广告和Banner广告等,最大化广…...
服务器安全——日志分析和扫描
如何通过访问日志查询被攻击 扫描攻击 攻击日志 GET /index?sindex/%5Cthink%5CModule/Action/Param/$%7Bphpinfo()%7D HTTP/1.1", host: "主机", referrer: "主机sindex/\think\Module/Action/Param/${phpinfo()}" 攻击日志文件 .error.log sql注…...
vue3读取webrtc-stream 视频流
一.首先下载webrtc-stream,方便自己本地搭建视频流服务 https://download.csdn.net/download/cyw8998/90373521 解压后,启动命令 webrtc-streamer.exe -H 127.0.0.1:8020 二.vue3代码如下 <template><h1>video</h1><video id&…...
周考考题(学习自用)
1.查询student表中name叫张某的信息 select * from student where name张某; 2.写出char和varchar类型的区别 1)char存储固定长度的字符串,varchar存储可变长度的字符串(在实际长度的字符串上加上一个字节用于存储字符串长度)&a…...
ubuntu 22.04 安装vsftpd服务
先决条件,确保你已经配置好了存储库。 安装vsftpd 为了方便实验,我已经切换到了root用户。 rootlocal:~# apt-get install vsftpd修改配置 配置文件在 /etc/vsftpd.conf rootlocal:~# grep -vE ^#|^$ /etc/vsftpd.conf listenNO listen_ipv6YES anonymou…...
K-均值(K-means)聚类算法
K-均值(K-means)聚类算法是一种常用的无监督学习算法,用于将数据集分成 K 个簇(clusters)。该算法的基本思想是将数据点分为 K 个簇,使得每个数据点所属的簇内部的数据点之间的相似度最大化,而不…...
STM32F407通过FSMC扩展外部SRAM和NAND FLASH
1 扩展外部SRAM 1.1 地址情况 FSMC控制器的存储区分为4个区(Bank),每个区256MB。其中,Bank1可以用于连接SRAM、NOR FLASH、PSRAM,还可以连接TFT LCD。Bank1的地址范围是0x60000000~0x6FFFFFFF。Bank1又分为4个子区,每…...
AndroidStudio查看Sqlite和SharedPreference
1.查看Sqlite 使用App Inspection,这是个好东西 打开方式:View → Tool Windows → App Inspection 界面如图: App inspection不但可以看Sqlite还可以抓包network和background task连抓包工具都省了。 非常好使 2.查看sharedPreference 使…...
Elasticsearch:15 年来致力于索引一切,找到重要内容
作者:来自 Elastic Shay Banon 及 Philipp Krenn Elasticsearch 刚刚 15 岁了!回顾过去 15 年的索引和搜索,并展望未来 15 年的相关内容。 Elasticsearch 刚刚成立 15 周年。一切始于 2010 年 2 月的一篇公告博客文章(带有标志性的…...
信呼OA办公系统sql注入漏洞分析
漏洞描述 信呼OA办公系统uploadAction存在SQL注入漏洞,攻击者可利用该漏洞获取数据库敏感信息。 环境搭建 源码下载地址:https://github.com/rainrocka/xinhu 下载后解压到本地网站根目录下,配置好数据库,然后安装即可 默认密…...
TDengine 客户端连接工具 taos-Cli
简介工具获取运行命令行参数 基础参数高级参数 数据导出/导入 数据导出数据导入 执行 SQL 脚本使用小技巧 TAB 键自动补全设置字符列显示宽度其它 错误代码表 简介 TDengine 命令行工具(以下简称 TDengine CLI)是用户操作 TDengine 实例并与之交互最简…...
