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

Spring进阶:掌控Bean的作用域与生命周期

在上一篇文章中,我们了解了Spring IoC容器如何接管对象的创建和依赖注入,实现了松耦合。容器创建并管理的对象,我们称之为Bean

但是,容器仅仅是创建Bean就够了吗?显然不是。我们还需要关心:

  1. 这个Bean在容器中应该存在多少个实例? (是全局唯一,还是每次请求都创建一个新的?)

  2. 这个Bean从创建到销毁会经历哪些阶段? (我们能否在特定阶段执行一些自定义逻辑?)

这就是我们今天要探讨的核心内容:Bean的作用域(Scope) 和 Bean的生命周期(Lifecycle)

一、Bean的作用域 (Scope):定义实例的存在范围

Bean的作用域定义了Spring容器根据Bean定义创建的实例数量以及这些实例的共享范围。简单来说,它决定了当你向容器请求一个Bean时,是返回一个已存在的共享实例,还是创建一个全新的实例。

Spring框架定义了多种作用域,最核心和常用的有以下几种:

  1. Singleton (单例作用域 - 默认)

    • 定义:在一个Spring IoC容器中,无论你请求多少次该Bean(通过getBean()或依赖注入),只会存在一个共享的Bean实例

    • 特点:容器启动时(非懒加载情况下)就会创建这个单例Bean,之后所有对该Bean的请求都会返回这同一个实例。它是Spring的默认作用域

    • 适用场景:无状态的Bean,如Service层对象、Repository层对象、工具类、配置类等。这些Bean通常不持有与特定请求相关的状态,可以被多线程安全地共享。

    • 代码示例

      import org.springframework.stereotype.Service;
      // 默认就是singleton,可以省略 @Scope("singleton")
      // import org.springframework.context.annotation.Scope;
      // @Scope("singleton")
      @Service
      public class UserService {// ... 无状态的方法 ...public User findUser(long id) {System.out.println("Fetching user " + id + " using instance: " + this.hashCode());// ... 实际逻辑 ...return new User(id, "SingletonUser");}
      }
    • 注意:由于是共享实例,如果单例Bean持有可变状态(成员变量),必须特别注意线程安全问题。通常应设计为无状态或使用线程安全的方式管理状态(如ThreadLocal,或委托给其他有状态的prototype bean)。

  2. Prototype (原型作用域)

    • 定义:每次向Spring容器请求该Bean时,容器都会创建一个全新的Bean实例并返回。

    • 特点:容器负责创建和注入依赖,但一旦将实例交给请求方,容器就不再跟踪和管理该实例的完整生命周期(特别是销毁阶段)。你需要自己负责后续的资源释放(如果需要)。

    • 适用场景:有状态的Bean,即那些需要为每个请求或会话维护独立状态的对象。例如,一个代表用户购物车或者某个具体操作上下文的对象。

    • 代码示例

      import org.springframework.context.annotation.Scope;
      import org.springframework.stereotype.Component;
      import org.springframework.beans.factory.config.ConfigurableBeanFactory; // 推荐使用常量@Component
      @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 或者 @Scope("prototype")
      public class UserActionContext {private long userId;private String action;public UserActionContext() {System.out.println("Creating new UserActionContext instance: " + this.hashCode());}// Getters and Setters for state...public void setUserId(long userId) { this.userId = userId; }public void setAction(String action) { this.action = action; }// ...
      }
    • 注意:频繁创建和销毁prototype Bean可能会带来性能开销。同时,Spring容器不会自动调用prototype Bean的销毁回调方法(如@PreDestroy或DisposableBean.destroy),需要使用者自行管理。

  3. Web应用专属作用域 (仅在Web环境有效)

    • Request: 每个HTTP请求都会创建一个新的Bean实例。该实例仅在当前HTTP请求内有效。

    • Session: 每个HTTP Session会创建一个新的Bean实例。该实例在当前HTTP Session内共享。

    • Application: 在整个Web应用(ServletContext)生命周期内,只创建一个Bean实例。类似于singleton,但作用范围是ServletContext。

    • WebSocket: 在WebSocket生命周期内,只创建一个Bean实例。

    这些作用域在构建Web应用程序时非常有用,用于管理与请求、会话等相关的状态。我们将在后续的Web开发专题中更详细地探讨它们。使用它们需要你的应用是一个Web应用,并且进行了相应的配置(例如,在Spring MVC或Spring WebFlux环境)。

    如何指定作用域?

    • 注解方式: 使用@Scope注解,如 @Scope("prototype") 或 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)。

    • XML方式: 在<bean>标签中使用scope属性,如 <bean id="myBean" class="..." scope="prototype"/>。

二、Bean的生命周期 (Lifecycle):从诞生到消亡

Spring容器不仅创建Bean,还管理它们的整个生命周期,从实例化到最终销毁。理解这个过程,以及如何在关键节点介入,对于进行资源初始化、资源释放、逻辑验证等操作至关重要。

一个典型的(主要针对Singleton Bean)生命周期包含以下关键阶段:

  1. 实例化 (Instantiation): Spring容器根据Bean定义(XML、注解等)找到对应的类,通过Java反射机制调用构造函数创建Bean的实例。

  2. 填充属性 (Populate Properties): 容器分析Bean的依赖关系(DI),通过Setter方法或直接字段注入(或者构造器注入在实例化阶段完成)将依赖的Bean或其他配置值(如@Value注解的值)设置到Bean实例的属性中。

  3. 初始化 (Initialization): 这是Bean生命周期中一个非常重要的阶段,允许开发者执行自定义的初始化逻辑。Spring提供了多种方式介入:

    • 执行Aware接口方法: 如果Bean实现了特定的Aware接口(如BeanNameAware, BeanFactoryAware, ApplicationContextAware),Spring会调用相应的方法,将容器自身的一些资源注入给Bean。例如,setBeanName()会在setBeanFactory()之前调用。

    • 执行BeanPostProcessor前置处理: BeanPostProcessor接口的postProcessBeforeInitialization方法会被调用。这是一个全局性的扩展点,可以对容器中所有(或筛选后的)Bean进行处理。

    • 执行初始化回调:

      • 如果Bean实现了InitializingBean接口,其afterPropertiesSet()方法会被调用。

      • 如果Bean定义中通过@Bean(initMethod="...")或XML的init-method属性指定了自定义初始化方法,该方法会被调用。

      • 如果Bean的方法使用了@PostConstruct注解(JSR-250标准),该方法会被调用。这是目前推荐的方式,因为它不依赖Spring特定接口,更加标准。
        (执行顺序: @PostConstruct -> InitializingBean.afterPropertiesSet -> init-method)

    • 执行BeanPostProcessor后置处理: BeanPostProcessor接口的postProcessAfterInitialization方法会被调用。常用于对Bean进行代理包装(如AOP实现)。

  4. Bean可用 (Bean is Ready): 经过以上步骤,Bean实例已经完全创建并初始化好,可以被应用程序使用了。对于Singleton Bean,它会驻留在容器的单例缓存中。

  5. 销毁 (Destruction): 当Spring容器关闭时(或者对于非Singleton作用域,在特定条件下),容器会管理Bean的销毁过程。同样提供了多种回调机制:

    • 执行销毁回调:

      • 如果Bean实现了DisposableBean接口,其destroy()方法会被调用。

      • 如果Bean定义中通过@Bean(destroyMethod="...")或XML的destroy-method属性指定了自定义销毁方法,该方法会被调用。

      • 如果Bean的方法使用了@PreDestroy注解(JSR-250标准),该方法会被调用。这也是目前推荐的方式
        (执行顺序: @PreDestroy -> DisposableBean.destroy -> destroy-method)

生命周期回调示例 (@PostConstruct 和 @PreDestroy)

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;@Component // 默认是 singleton
public class ResourceHandler {public ResourceHandler() {System.out.println("1. Constructor: ResourceHandler instance created.");}@PostConstruct // 初始化回调public void init() {System.out.println("2. @PostConstruct: Initializing resources...");// 例如:建立数据库连接、加载配置文件、启动后台线程等}public void useResource() {System.out.println("3. Using ResourceHandler...");}@PreDestroy // 销毁回调public void cleanup() {System.out.println("4. @PreDestroy: Cleaning up resources...");// 例如:关闭数据库连接、释放文件句柄、停止后台线程等}
}// --- 在Spring应用中 ---
// ApplicationContext context = ... ;
// ResourceHandler handler = context.getBean(ResourceHandler.class);
// handler.useResource();
// ((ConfigurableApplicationContext) context).close(); // 关闭容器时会触发 @PreDestroy

输出大致顺序:

  1. Constructor: ResourceHandler instance created.

  2. @PostConstruct: Initializing resources...

  3. Using ResourceHandler...

  4. (当容器关闭时) @PreDestroy: Cleaning up resources...

注意:

  • prototype作用域的Bean,Spring容器在创建并交给调用者后,不会负责其后续的销毁回调(@PreDestroy, DisposableBean, destroy-method)。如果prototype Bean需要释放资源,需要调用者手动处理,或者使用BeanPostProcessor等高级技巧来管理。

三、作用域与生命周期的交互

  • Singleton Bean: 经历完整的生命周期,由Spring容器严格管理其创建、初始化和销毁。

  • Prototype Bean: 每次请求都创建新实例,执行实例化、属性填充、初始化回调(如@PostConstruct),然后交给请求方。销毁阶段不由容器管理

  • Web作用域 Bean: 生命周期与对应的Web范围(Request、Session等)绑定。当范围结束时,容器负责销毁这些Bean并执行销毁回调。

四、为什么理解作用域和生命周期很重要?

  • 资源管理: 通过生命周期回调(特别是@PostConstruct和@PreDestroy),可以在Bean创建后初始化资源(如连接池、文件句柄),在Bean销毁前释放资源,避免内存泄漏或资源枯竭。

  • 状态管理: 正确选择作用域(singleton vs prototype等)是管理Bean状态的关键。误用singleton处理每个请求都不同的状态会导致数据错乱和线程安全问题。

  • 性能考量: singleton性能较好(实例复用),而prototype涉及实例创建销毁开销。需要根据场景权衡。

  • 调试与问题排查: 了解Bean的创建和销毁过程有助于定位配置错误、循环依赖、初始化失败等问题。

  • 框架扩展: BeanPostProcessor等生命周期接口是Spring框架实现AOP、事务管理等功能的基础,理解生命周期有助于理解这些高级特性。

五、总结

Spring Bean的作用域(Scope)定义了Bean实例的共享策略和存在范围(如singleton, prototype),而生命周期(Lifecycle)则描述了Bean从创建到销毁所经历的各个阶段以及开发者可以介入的时机(通过@PostConstruct, @PreDestroy等回调)。

熟练掌握这两个概念,能够让你更精确地控制Bean的行为,更有效地管理资源,编写出更健壮、高效的Spring应用程序。它们是深入理解Spring内部机制和进行高级开发的必备知识。


相关文章:

Spring进阶:掌控Bean的作用域与生命周期

在上一篇文章中&#xff0c;我们了解了Spring IoC容器如何接管对象的创建和依赖注入&#xff0c;实现了松耦合。容器创建并管理的对象&#xff0c;我们称之为Bean。 但是&#xff0c;容器仅仅是创建Bean就够了吗&#xff1f;显然不是。我们还需要关心&#xff1a; 这个Bean在容…...

【Leetcode-Hot100】移动零

题目 解答 首先&#xff0c;使用的解题思路是&#xff1a;使用两个指针&#xff0c;分别指向数组的第一个0元素位置&#xff0c;以该元素位置1为起始点寻找接下来第一个非0元素位置。二者确定后&#xff0c;对其进行交换。随后继续寻找下一个0元素位置。重复上述操作。 但第一…...

安装 Calico 的两种主流方式对比

本文对比了 Calico 的两种主流安装方式&#xff1a; 使用 calico.yaml 的 Manifest 安装方式使用 Tigera Operator&#xff08;tigera-operator.yaml custom-resources.yaml&#xff09;安装方式 ✅ 1. 使用 Manifest 方式安装&#xff08;直接部署 calico.yaml&#xff09; …...

leetcode_203. 移除链表元素_java

203. 移除链表元素https://leetcode.cn/problems/remove-linked-list-elements/ 1、题目 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head …...

常见算法模板总结

文章目录 一、二叉树1. DFS2. BFS 二、回溯模板三、记忆化搜索四、动态规划1. 01背包朴素版本滚动数组优化 2. 完全背包朴素版本滚动数组优化 3. 最长递增子序列LIS朴素版本贪心二分优化 4. 最长公共子序列5. 最长回文子串 五、滑动窗口六、二分查找七、单调栈八、单调队列九、…...

UE5学习笔记 FPS游戏制作44 统一UI大小 sizeBox

如果我们希望多个类似的UI大小一样&#xff0c;例如不同菜单的标题&#xff0c;可以使用sizeBox组件 我们在标题控件上&#xff0c;用sizeBox包裹所有子物体 然后指定他的最小宽高&#xff0c;或最大宽高 如果指定的是最小宽高&#xff0c;当子元素&#xff08;如图片&#xf…...

# 基于BERT的文本分类

基于BERT的文本分类项目的实现 一、项目背景 该文本分类项目主要是情感分析&#xff0c;二分类问题&#xff0c;以下是大致流程及部分代码示例&#xff1a; 二、数据集介绍 2.1 数据集基本信息 数据集自定义类型二分类&#xff08;正面/负面&#xff09;样本量训练集 验证…...

C++学习之服务器EPOLL模型、处理客户端请求、向客户端回复数、向客户端发送文件

目录 1.启动epoll模型 2.和客户端建立新连接 3.接受客户端Http请求数据 4.代码回顾从接受的数据中读出请求行 5.请求行解析 6.正则表达式以及匹配 7.解析请求行以及后续处理 8.对path处理说明 9.如何回复响应数据 10.对文件对应content-type如何查询 11.服务器处理流…...

BUUCTF-web刷题篇(17)

26.BabyUpload 源码&#xff1a;https://github.com/imaginiso/GXY_CTF/tree/master/Web/babyupload 查看题目源码&#xff1a; 写着&#xff1a;SetHandler application/x-httpd-php 通过源码可以看出这道文件上传题目主要还是考察.htaccess配置文件的特性&#xff0c;倘若…...

国网B接口协议调阅实时视频接口流程详解以及检索失败原因(电网B接口)

文章目录 一、B接口协议调阅实时视频接口介绍B.6.1 接口描述B.6.2 接口流程B.6.3 接口参数B.6.3.1 SIP头字段B.6.3.2 SIP响应码B.6.3.3 SDP参数定义B.6.3.4 RTP动态Payload定义 B.6.4 消息示例B.6.4.1 调阅实时视频请求B.6.4.2 调阅实时视频请求响应 二、B接口调阅实时视频失败…...

windows11下pytorch(cpu)安装

先装anaconda 见最下方 Pytorch 官网&#xff1a;PyTorch 找到下图&#xff08;不要求版本一样&#xff09;&#xff08;我的电脑是集显&#xff08;有navdia的装gpu&#xff09;&#xff0c;装cpu&#xff09; 查看已有环境列表 创建环境 conda create –n 虚拟环境名字(…...

NVR接入录像回放平台用EasyCVR打造地下车库安防:大型商居安全优选方案

一、背景分析 随着居民生活品质的提升&#xff0c;大型商业建筑和住宅小区纷纷配套建设地下停车库。但是地下车库盗窃、失火、恶意毁坏车辆、外部人员随意进出等事件频发&#xff0c;部署视频监控系统成为保障地下车库的安全关键举措。 目前&#xff0c;很多商业和住宅都会在…...

玻璃期货数据下载与分析:Python金融实战分享

期货数据下载与分析&#xff1a;Python实战分享 引言 在金融市场中&#xff0c;期货分析是一项重要的工作&#xff0c;而获取准确且及时的数据是进行有效分析的基础。今天&#xff0c;我们将深入探讨一段使用Python编写的代码&#xff0c;该代码用于从郑州商品交易所&#xf…...

excel常见错误包括(#N/A、#VALUE!、#REF!、#DIV/0!、#NUM!、#NAME?、#NULL! )

目录 1. #N/A2. #VALUE!3. #REF!4. #DIV/0!5. #NUM!6. #NAME?7. #NULL!8.图表总结 在 Excel 中&#xff0c;可能会遇到以下常见的错误值&#xff0c;每个都有特定的含义和成因&#xff1a; 1. #N/A 含义&#xff1a; 表示“Not Available”&#xff08;不可用&#xff09;。…...

乾元通渠道商中标川藏铁路西藏救援队应急救援装备项目

乾元通渠道商中标川藏铁路西藏救援队应急救援装备项目&#xff0c;项目内通信指挥车基于最新一代应急指挥车解决方案打造&#xff0c;配合乾元通自研的车载多链路聚合路由及系统&#xff0c;主要用途为保障应急通讯&#xff0c;满足任务执行时指挥协调、通信联络及数据传输的要…...

数学知识——矩阵乘法

使用矩阵快速幂优化递推问题 对于一个递推问题&#xff0c;如递推式的每一项系数都为常数&#xff0c;我们可以使用矩阵快速幂来对算法进行优化。 一般形式为&#xff1a; F n F 1 A n − 1 F_nF_1A^{n-1} Fn​F1​An−1 由于递推式的每一项系数都为常数&#xff0c;因此对…...

左右开弓策略思路

一、策略概述 本策略是一种基于多种技术指标的复杂交易策略&#xff0c;包括自定义指标计算、过滤平滑处理以及交易信号生成。 该策略通过不同的交易平台代码段实现&#xff0c;旨在通过分析历史价格数据来预测未来价格走势&#xff0c;并据此生成交易信号。 二、主要技术指标…...

将jar包制作成deb一键安装包

文章目录 准备环境准备deb包结构构建Deb包测试安装常用操作命令 本文介绍如何将java运行环境、jar程序一起打包成一个deb格式的安装包&#xff0c;创建桌面图标&#xff0c;通过点击图标可使用系统自带浏览器快捷访问web服务的URL&#xff0c;同时注册服务并配置好开机自启。 准…...

Java 常用安全框架的 授权模型 对比分析,涵盖 RBAC、ABAC、ACL、基于权限/角色 等模型,结合框架实现方式、适用场景和优缺点进行详细说明

以下是 Java 常用安全框架的 授权模型 对比分析&#xff0c;涵盖 RBAC、ABAC、ACL、基于权限/角色 等模型&#xff0c;结合框架实现方式、适用场景和优缺点进行详细说明&#xff1a; 1. 授权模型类型与定义 模型名称定义特点RBAC&#xff08;基于角色的访问控制&#xff09;通…...

aws平台练习

注册 AWS 账户 访问 AWS 官方网站&#xff0c;点击“免费注册”按钮&#xff0c;按照提示完成账户注册&#xff1a; 提供电子邮件地址、密码和电话号码。 验证身份&#xff08;可能需要手机验证码&#xff09;。 设置 billing 信息。 2. 登录 AWS 管理控制台 使用注册的邮箱和…...

力扣DAY40-45 | 热100 | 二叉树:直径、层次遍历、有序数组->二叉搜索树、验证二叉搜索树、二叉搜索树中第K小的元素、右视图

前言 简单、中等 √ 好久没更了&#xff0c;感觉二叉树来回就那些。有点变懒要警醒&#xff0c;不能止步于笨方法&#xff01;&#xff01; 二叉树的直径 我的题解 遍历每个节点&#xff0c;左节点最大深度右节点最大深度当前节点当前节点为中心的直径。如果左节点深度更大…...

【MYSQL从入门到精通】数据类型及建表

一些基础操作语句 1.使用客户端工具连接数据库服务器&#xff1a;mysql -uroot -p 2.查看所有数据库&#xff1a;show databases; 3.创建属于自己的数据库&#xff1a; create database 数据库名;create database if not exists 数据库名; 强烈建议大家在建立数据库时指定编…...

【动态规划】 深入动态规划—两个数组的dp问题

文章目录 前言例题一、最长公共子序列二、不相交的线三、不同的子序列四、通配符匹配五、交错字符串六、两个字符串的最小ASCII删除和七、最长重复子数组 结语 前言 问题本质 它主要围绕着给定的两个数组展开&#xff0c;旨在通过对这两个数组元素间关系的分析&#xff0c;找出…...

结合大语言模型整理叙述并生成思维导图的思路

楔子 我比较喜欢长篇大论。这在代理律师界被视为一种禁忌。 我高中一年级的时候因为入学成绩好&#xff08;所在县榜眼名次&#xff09;&#xff0c;直接被所在班的班主任任命为班长。我其实不喜欢这个岗位。因为老师一来就要提前注意到&#xff0c;要及时喊“起立”、英语课…...

Kotlin学习

kotlin android 开源,Kotlin开源项目集合_晚安 呼-华为开发者空间 干货来袭&#xff0c;推荐几款开源的Kotlin的Android项目 https://zhuanlan.zhihu.com/p/536789267 【已解决】 ubuntu apt-get update连不上dl.google.com_为什么不能ping谷歌-CSDN博客...

若依前后端分离版本从mysql切换到postgresql数据库

一、修改依赖&#xff1a; 修改admin模块pom.xml中的依赖,屏蔽或删除mysql依赖&#xff0c;增加postgresql依赖。 <!-- Mysql驱动包 --> <!--<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId> &l…...

【力扣hot100题】(073)数组中的第K个最大元素

花了两天时间搞明白答案的快速排序和堆排序。 两种都写了一遍&#xff0c;感觉堆排序更简单很多。 两种都记录一下&#xff0c;包括具体方法和易错点。 快速排序 class Solution { public:vector<int> nums;int quicksort(int left,int right,int k){if(leftright) r…...

【AAOS】【源码分析】CarAudioService(二)-- 功能介绍

汽车音频是 Android 汽车操作系统 (AAOS) 的一项功能,允许车辆播放信息娱乐声音,例如媒体、导航和通信。AAOS 不负责具有严格可用性和时间要求的铃声和警告,因为这些声音通常由车辆的硬件处理。将汽车音频服务集成在汽车中,彻底改变了驾驶体验,为驾驶员和乘客提供了音乐、…...

mapbox基础,加载F4Map二维地图

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性二、🍀F4Map 简介2.1 ☘️技术特点2.2 ☘️核…...

Android:Android Studio右侧Gradle没有assembleRelease等选项

旧版as是“Do not build Gradle task list during Gradle sync” 操作这个选项。 参考这篇文章&#xff1a;Android Studio Gradle中没有Task任务&#xff0c;没有Assemble任务&#xff0c;不能方便导出aar包_gradle 没有task-CSDN博客 在as2024版本中&#xff0c;打开Setting…...