当前位置: 首页 > news >正文

【Spring Bean的生命周期】

文章目录

    • Spring Bean的生命周期
      • 实例化
        • 构造器实例化
        • 工厂方法实例化
      • 属性赋值
        • XML方式
        • 注解方式
      • 初始化
        • postProcessBeforeInitialization()和postProcessAfterInitialization()
        • InitializingBean接口的afterPropertiesSet()方法
        • 通过@Bean注解定义的初始化方法
        • 使用@PostConstruct注解标注的初始化方法
      • 销毁
        • 配置destroy-method方法来销毁Bean
        • 实现DisposableBean接口来销毁Bean

Spring Bean的生命周期

Spring Bean的生命周期分为四个阶段:实例化、属性赋值、初始化和销毁。

实例化

构造器实例化

通过Java类的构造函数实例化Bean,利用Java反射机制,调用bean对应类的构造方法进行实例化。

在XML文件中,可以使用标签的class属性指定要实例化的Bean类。当容器启动时,容器会根据class属性的全限定类名使用反射机制实例化Bean。示例代码:

<bean id="myBean" class="com.example.MyBean"/>

在注解方式中,@Component、@Service、@Controller等注解本质上是Java类的元数据,Spring框架在启动时会扫描指定包路径下所有的类,将被标注了这些注解的类通过反射机制实例化,并将实例化后的对象注册到Spring容器中,以供程序使用。例如:

@Component
public class MyBean {// class implementation
}

工厂方法实例化

在Spring配置文件中,可以定义一个工厂类,该工厂类中有一个静态方法可以创建Bean对象,并且可以指定返回值和参数,Spring框架会在启动时自动调用该静态方法来创建Bean对象。这种方式类似于单例模式,因为每个Bean对象只会被实例化一次,并且可以在整个应用程序中共享。配置示例代码如下:

<bean id="myBean" class="com.example.MyBeanFactory" factory-method="createMyBean"/>

代码如下:

public class MyBeanFactory {public static MyBean createMyBean() {return new MyBean();}
}

属性赋值

属性赋值是在实例化Bean后通过BeanPostProcessor接口实现对Bean的属性赋值。

XML方式

在XML文件中定义Bean以及其属性的值来配置Bean,在Spring容器启动时,会解析这些XML文件并根据其定义的配置创建相应的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-4.3.xsd"><!-- 定义一个名为 person 的 Bean --><bean id="person" class="com.example.Person"><property name="name" value="John"/><property name="age" value="25"/></bean></beans>

上述 XML 配置文件定义了一个名为 person 的 Bean,它的类为 com.example.Person,并设置了 name 和 age 两个属性的值。

在 Spring 容器启动时,会解析该 XML 配置文件,创建一个名为 person 的 Bean 实例,并将其属性值设置为 name=John,age=25。可以使用以下代码获取该 Bean:

ApplicationContext context = new ClassPathXmlApplicationContext("path/to/config.xml");
Person person = (Person) context.getBean("person");

这样就可以使用 person 对象来访问其属性值了。

注解方式

使用@Autowired或@Value注解进行属性赋值。这些注解的实现原理也是基于BeanPostProcessor接口实现的。

@Autowired注解是根据属性的类型来进行自动注入的,如果Spring容器中存在该类型的Bean,则会自动将其注入到对应的属性中。如果存在多个同类型的Bean,则可以使用@Qualifier注解来指定要注入的Bean的名称。

好的,以下是一个简单的示例:

假设我们有一个接口UserService,有两个实现类UserServiceAUserServiceB

public interface UserService {void addUser(String username, String password);
}
@Service("userServiceA")
public class UserServiceA implements UserService {public void addUser(String username, String password) {System.out.println("User " + username + " is added by UserServiceA");}
}
@Service("userServiceB")
public class UserServiceB implements UserService {public void addUser(String username, String password) {System.out.println("User " + username + " is added by UserServiceB");}
}

现在我们有一个需要依赖UserService的类UserController

@Controller
public class UserController {@Autowiredprivate UserService userService;public void addUser(String username, String password) {userService.addUser(username, password);}
}

当Spring容器扫描到UserController的时候,会发现它有一个属性userService需要注入,然后根据该属性的类型UserService自动从容器中查找对应的Bean。由于容器中有UserServiceAUserServiceB两个实现类,所以Spring会报错,无法决定要使用哪个实现类进行注入。

为了解决这个问题,我们可以使用@Qualifier注解,将要注入的Bean的名称告诉Spring容器。例如:

@Controller
public class UserController {@Autowired@Qualifier("userServiceB")private UserService userService;public void addUser(String username, String password) {userService.addUser(username, password);}
}

这样,Spring容器就会自动将名称为userServiceBUserService实现类注入到UserControlleruserService属性中。

@Value注解则是根据属性的值来进行注入的,可以使用${}或#{ }来引用配置文件中的属性值,也可以直接指定一个固定的值。

@Value("${jdbc.url}")
private String url;

通过@Value注解,Spring会将配置文件中名为"jdbc.url"的属性值注入到url属性中。

初始化

初始化是Spring Bean生命周期的第三个阶段,它包括两个过程:初始化前和初始化后,BeanPostProcessor接口在执行初始化方法之前和之后定义了两个方法:postProcessBeforeInitialization()和postProcessAfterInitialization()。

postProcessBeforeInitialization()和postProcessAfterInitialization()

postProcessBeforeInitialization()方法在执行Bean的初始化方法之前被调用,可以对Bean进行自定义的前处理操作。例如,可以修改Bean的属性值、增加一些代理逻辑等等。这时的Bean还没有执行初始化方法,也就是说Bean还没有完全初始化。这个方法常常用于注册一些事件监听器、给Bean进行数据校验等。

postProcessAfterInitialization()方法在执行Bean的初始化方法之后被调用,可以对Bean进行自定义的后处理操作。例如,可以对Bean做一些额外的检查、修改某些属性值等等。这时的Bean已经执行了初始化方法,并且已经完全初始化。这个方法常常用于增强Bean的能力或者为Bean提供一些额外服务(如数据缓存、资源池等)。

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {//在Bean的初始化方法之前执行的自定义操作if (bean instanceof MyBean) {((MyBean)bean).setProperty("new value");//注册事件监听器if (bean instanceof MyBean) {MyBean myBean = (MyBean) bean;myBean.addEventListener(new MyEventListener());}//给Bean进行数据校验if (bean instanceof MyBean) {MyBean myBean = (MyBean) bean;Validator validator = new MyBeanValidator();validator.validate(myBean);}}return bean;}public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {//在Bean的初始化方法之后执行的自定义操作if (bean instanceof MyBean) {//增强Bean的能力//检查if (bean instanceof MyBean) {MyBean myBean = (MyBean)bean;if (myBean.getName() == null) {throw new IllegalArgumentException("Name cannot be null");}}//修改某些属性值if (bean instanceof MyBean) {MyBean myBean = (MyBean)bean;myBean.setProperty("new value");}//数据缓存if (bean instanceof MyBean) {MyBean myBean = (MyBean)bean;CacheManager cacheMgr = CacheManager.getInstance();Cache cache = cacheMgr.getCache("myCache");cache.put(myBean.getId(), myBean);}//资源池if (bean instanceof MyBean) {MyBean myBean = (MyBean)bean;ConnectionPool pool = ConnectionPool.getInstance();myBean.setConnection(pool.getConnection());}}return bean;}
}
//实现了ApplicationListener接口,并重写了onApplicationEvent方法来处理ContextRefreshedEvent事件
public class MyEventListener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {if (event.getSource() instanceof MyBean) {MyBean myBean = (MyBean) event.getSource();System.out.println("MyBean " + myBean.getName() + " has been refreshed.");}}
}

InitializingBean接口的afterPropertiesSet()方法

Spring容器在创建Bean实例之后,会自动调用InitializingBean接口的afterPropertiesSet()方法完成Bean的初始化。

以下是一个实现InitializingBean接口的示例代码:

import org.springframework.beans.factory.InitializingBean;public class MyBean implements InitializingBean {private String message;public void setMessage(String message) {this.message = message;}@Overridepublic void afterPropertiesSet() throws Exception {// 在这里初始化BeanSystem.out.println("Initializing MyBean...");System.out.println("MyBean message: " + message);}
}

在XML配置文件中配置该Bean的属性和初始化方法:

<bean id="myBean" class="com.example.MyBean"><property name="message" value="Hello, World!"/><property name="initMethod" value="afterPropertiesSet"/>
</bean>

在上面的配置中,我们设置了Bean的属性message为“Hello, World!”,并设置了initMethod属性为afterPropertiesSet,这样在Bean创建完成后,会自动调用MyBean实现的afterPropertiesSet()方法完成Bean的初始化。

除了实现InitializingBean接口,还可以通过@Bean注解中的initMethod属性,或者使用@PostConstruct注解标注的初始化方法。示例代码:

通过@Bean注解定义的初始化方法

@Configuration
public class AppConfig {@Bean(initMethod = "init")public MyBean myBean() {return new MyBean();}
}public class MyBean {public void init() {// initialization code}
}

使用@PostConstruct注解标注的初始化方法

public class MyBean {@PostConstructpublic void init() {// initialization code}
}

销毁

销毁是Spring Bean生命周期的最后一个阶段,它是通过实现DisposableBean接口或通过配置destroy-method方法来实现。实现DisposableBean接口,需要实现destroy()方法,该方法会在Bean销毁前被调用。在容器关闭之前,Spring会先销毁Bean,并回调Bean的destroy()方法。

配置destroy-method方法来销毁Bean

在XML文件中,可以使用destroy-method属性指定Bean的销毁方法。Spring容器会在销毁Bean之前调用这个方法。

<bean id="exampleBean" class="com.example.ExampleBean" destroy-method="cleanup"><property name="name" value="John" />
</bean>

在上面的代码中,ExampleBean类有一个名为cleanup的方法,它将在Bean销毁时被调用。在XML文件中,通过配置destroy-method属性来指定销毁方法。

实现DisposableBean接口来销毁Bean

通过实现DisposableBean接口,Bean类可以在调用destroy()方法之前实现销毁操作。该方法会在Bean销毁之前调用。销毁的具体过程可以自定义实现。在销毁Bean之前,需要先关闭应用上下文,释放Bean占用的资源。

public class ExampleBean implements DisposableBean {private String name;public void setName(String name) {this.name = name;}@Overridepublic void destroy() throws Exception {System.out.println("Cleaning up resources for " + name);}
}

在上面的代码中,ExampleBean类实现了DisposableBean接口,并重写了destroy()方法。在该方法中,可以自定义销毁Bean的逻辑。在Bean销毁之前,Spring容器会回调该方法。

相关文章:

【Spring Bean的生命周期】

文章目录 Spring Bean的生命周期实例化构造器实例化工厂方法实例化 属性赋值XML方式注解方式 初始化postProcessBeforeInitialization()和postProcessAfterInitialization()InitializingBean接口的afterPropertiesSet()方法通过Bean注解定义的初始化方法使用PostConstruct注解标…...

信息化发展49

软件设计 1 、软件设计是需求分析的延伸与拓展。需求分析阶段解决“做什么” 的问题&#xff0c;而软件设计阶段解决“怎么做” 的问题。同时&#xff0c; 它也是系统实施的基础&#xff0c; 为系统实施工作做好铺垫。合理的软件设计方案既可以保证系统的质量&#xff0c; 也可…...

linux常用命令(4):mkdir命令(创建目录)

文章目录 一、命令简介二、命令格式三、常用示例 一、命令简介 mkdir&#xff08;make directories&#xff09;创建目录。 若指定目录不存在则创建目录。若指定目录已存在&#xff0c;则会提示已存在而不继续创建。 touch与mkdir的区别? 很多人可能会把这个搞混淆&#xff…...

企业架构LNMP学习笔记58

开始学习Tomcat&#xff1a; 学习目标和内容&#xff1a; 1&#xff09;能够描述Tomcat的使用场景&#xff1b; 2&#xff09;能够简单描述Tomcat的工作原理&#xff1b; 3&#xff09;能够实现部署安装Tomcat&#xff1b; 4&#xff09;能够实现和配置Tomcat的Server服务…...

[JAVAee]SpringBoot配置文件

配置文件的介绍 配置文件当中记录了许多重要的配置信息,例如: 数据库的连接信息(用户的账户与密码)项目的启动端口第三方系统的调用密匙用于记录问题产生的日志 在spring框架中一些特定的框架会自动调用配置文件中的配置信息来运用. 配置文件中的属性也起到了类似全局变量的…...

复制远程连接到Linux使用VIM打开的内容到Windows

我们经常是使用SSH工具远程连接到Linux服务器上进行工作&#xff0c;有时候需要将Linux下使用VIM打开的文件内容复制到Windows上来&#xff0c;默认情况下&#xff0c;可能会复制不了&#xff0c;因为VIM默认情况下是使用的set mousea的设置&#xff0c;它会让鼠标选中的时候进…...

左神算法之中级提升班(9)

目录 【案例1】 【题目描述】 【思路解析】 【代码实现】 【案例2】 【题目描述】 【思路解析 平凡解技巧 从业务中分析终止条件 重点】 【代码实现】 【案例3】 【题目描述】 【思路解析】 【案例4】 【题目描述】 【思路解析】 【代码实现】 【动态规划代码】…...

SmartNews 基于 Flink 的 Iceberg 实时数据湖实践

摘要&#xff1a;本文整理自 SmartNews 数据平台架构师 Apache Iceberg Contributor 戢清雨&#xff0c;在 Flink Forward Asia 2022 实时湖仓专场的分享。本篇内容主要分为五个部分&#xff1a; SmartNews 数据湖介绍基于 Icebergv1 格式的数据湖实践基于 Flink 实时更新的数据…...

websocket请求通过IteratorAggregate实现流式输出

对接国内讯飞星火模型&#xff0c;官方文档接口采用的是websocket跟国外chatgpt有些差异。 虽然官网给出一个简单demo通过while(true)&#xff0c;websocket的receive()可以实现逐条接受并输出给前端&#xff0c;但是通用和灵活度不高。不能兼容现有项目框架的流式输出。故模仿…...

《C和指针》笔记28:可变参数和stdarg宏

可变参数列表可以通过宏来实现&#xff0c;这些宏定义于stdarg.h头文件&#xff0c;它是标准库的一部分。这个头文件声明了一个类型va_list和三个宏——va_start、va_arg和va_end 。我们可以声明一个类型为va_list的变量&#xff0c;与这几个宏配合使用&#xff0c;访问参数的值…...

Matlab论文插图绘制模板第114期—带图形标记的图

之前的文章中&#xff0c;分享了Matlab带线标记的图&#xff1a; 带阴影标记的图&#xff1a; 带箭头标记的图&#xff1a; 进一步&#xff0c;分享一下带图形标记的图&#xff0c;先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『数据代码』已上传资源群中&…...

Python:用于有效对象管理的单例模式

1. 写在前面 在本文中&#xff0c;我们将介绍一种常用的软件设计模式 —— 单例模式。 通过示例&#xff0c;演示单例创建&#xff0c;并确保该实例在整个应用程序生命周期中保持一致。同时探讨它在 Python 中的目的、益处和实际应用。 关键点&#xff1a; 1、单例模式只有…...

【TCP】滑动窗口、流量控制 以及拥塞控制

滑动窗口、流量控制 以及拥塞控制 1. 滑动窗口&#xff08;效率机制&#xff09;2. 流量控制&#xff08;安全机制&#xff09;3. 拥塞控制&#xff08;安全机制&#xff09; 1. 滑动窗口&#xff08;效率机制&#xff09; TCP 使用 确认应答 策略&#xff0c;对每一个发送的数…...

Xilinx FPGA管脚约束语法规则(UCF和XDC文件)

文章目录 1. ISE环境&#xff08;UCF文件&#xff09;2. Vivado环境&#xff08;XDC文件&#xff09; 本文介绍ISE和Vivado管脚约束的语句使用&#xff0c;仅仅是管脚和电平状态指定&#xff0c;不包括时钟约束等其他语法。 ISE使用UCF文件格式&#xff0c;Vivado使用XDC文件&…...

服务网格和CI/CD集成:讨论服务网格在持续集成和持续交付中的应用。

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…...

代码随想录训练营第56天|583.两个字符串的删除操作,72.编辑距离

代码随想录训练营第56天|583.两个字符串的删除操作&#xff0c;72.编辑距离 583.两个字符串的删除操作文章思路代码 72.编辑距离文章思路代码 总结 583.两个字符串的删除操作 文章 代码随想录|0583.两个字符串的删除操作 思路 如果不按照编辑距离考虑的话&#xff0c;只需要…...

【JDK 8-Lambda】3.1 Java高级核心玩转 JDK8 Lambda 表达式

一、 什么是函数式编程 &#xff1f; 二、 什么是lambda表达式&#xff1f; 1. 先看两个示例 A.【创建线程】 B.【数组排序-降序】 2. lambda表达式特性 A. 使用场景(前提): B. 语法 (params) -> expression C. 参数列表 D. 方法体 F. 好处 一、 什么是函数式编…...

【C#】XML的基础知识以及读取XML文件

最近在学读取文件 目录 介绍特点结构XML的语法规则XML 命名规则 C#操作XML新建读取第一种第二种第三种 读取属性 介绍 XML (可扩展标记语言&#xff0c;eXtensible Markup Language) 是一种标记语言&#xff0c;它被设计用来传输和存储数据。 特点 可扩展性&#xff1a;由于…...

Immutable.js简介

引子 看一段大家熟悉的代码 const state {str: wwming,obj: {y: 1},arr: [1, 2, 3] } const newState stateconsole.log(newState state) // truenewState和state是相等的 原因&#xff1a; 由于js的对象和数组都是引用类型。所以newState的state实际上是指向于同一块内存…...

C语言进阶教程(位操作和进制数的表示)

文章目录 前言一、左移和右移二、清除对应的位为0和设置对应的位为11.设置对应的位为12.清除对应的位为0 三、进制数的表示四、& ^ | ~总结 前言 本篇文章给大家讲解一下C语言中的位操作&#xff0c;在嵌入式中位操作是经常需要使用的&#xff0c;那么下面就让我们来学习一…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...