当前位置: 首页 > 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;那么下面就让我们来学习一…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...