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

Spring | Bean 作用域和生命周期

一、通过一个案例来看 Bean 作用域的问题

Spring 是用来读取和存储 Bean,因此在 Spring 中 Bean 是最核心的操作资源,所以接下来我们深入学习⼀下 Bean 对象

假设现在有⼀个公共的 Bean,提供给 A 用户和 B 用户使用,然而在使用的途中 A 用户却 “悄悄” 地修改了公共 Bean 的数据,导致 B 用户在使用时发生了预期之外的逻辑错误

我们预期的结果是,公共 Bean 可以在各自的类中被修改,但不能影响到其他类

1、被修改的 Bean 案例

——公共 Bean:

@Component
public class Users {@Beanpublic User user1() {User user = new User();user.setId(1);user.setName("Java"); // 【重点:名称是 Java】return user;}
}

——A 用户使用时,进行了修改操作:

@Controller
public class BeanScopesController {@Autowiredprivate User user1;public User getUser1() {User user = user1;System.out.println("Bean 原 Name:" + user.getName());user.setName("悟空"); // 【重点:进行了修改操作】return user;}
}

——B 用户再去使用公共 Bean 的时候:

@Controller
public class BeanScopesController2 {@Autowiredprivate User user1;public User getUser1() {User user = user1;return user;}
}

——打印 A 用户和 B 用户公共 Bean 的值:

public class BeanScopesTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");BeanScopesController beanScopesController = context.getBean(BeanScopesController.class);System.out.println("A 对象修改之后 Name:" + beanScopesController.getUser1().toString());BeanScopesController2 beanScopesController2 = context.getBean(BeanScopesController2.class);System.out.println("B 对象读取到的 Name:" + beanScopesController2.getUser1().toString());}
}

——执行结果如下:

Bean 原 Name: Java (原来的值)
A 对象修改之后 Name: 1:悟空 (被 A 对象修改)
B 对象读取到的 Name: 1:悟空 (B 对象中也跟着被更新了)


2、原因分析

操作以上问题的原因是因为 Bean 默认情况下是单例状态(singleton),也就是所有⼈的使用的都是同⼀个对象,之前我们学单例模式的时候都知道,使用单例可以很⼤程度上提高性能,所以在 Spring 中Bean 的作用域默认也是 singleton 单例模式


二、作用域定义 Scope

限定程序中变量的可用范围叫做作用域,或者说在源代码中定义变量的某个区域就叫做作用域

而 Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式,⽐如 singleton 单例作用域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值


1、Bean 的 6 种作用域

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作用域。Spring有 6 种作用域,最后四种是基于 Spring MVC 生效的:

  1. singleton :单例作用域(默认)

  2. prototype :原型作用域(多例作用域)

  3. request :请求作用域(Spring MVC)

  4. session :回话作用域(Spring MVC)

  5. application :全局作用域(Spring MVC)

  6. websocket :HTTP WebSocket 作用域(Spring WebSocket)

注意后 4 种状态是 Spring MVC 中的值,在普通的 Spring 项目中只有前两种


2、设置 bean 作用域

@scope 标签既可以修饰方法,也可以修饰类,有两种设置方式:

  • 直接设置值:@Scope("prototype")
  • 类似枚举常量的设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

2.1、测试 Bean 默认是那种作用域

——UserBeans:

@Component
public class UserBeans {@Bean(name = "user1")public User getUser1() {User user = new User();user.setId(1);user.setName("zhangsan");return user;}@Bean(name = "user2")public User getUser2() {User user = new User();user.setId(2);user.setName("lisi");return user;}@Bean(name = "user3")public User getUser3() {User user = new User();user.setId(3);user.setName("Java");return user;}
}

——创建类 BeanScope1:

@Component
public class BeanScope1 {@Autowired // 注入 user3private User user3;public User getUser3() {User user = user3;user.setName("八戒");return user;}
}

——创建类 BeanScope2:

@Component
public class BeanScope2 {@Autowiredprivate User user3;public User getUser3() {return user3;}
}

——测试:

public class App {public static void main(String[] args) {ApplicationContext context = newClassPathXmlApplicationContext("spring-config.xml");BeanScope1 beanScope1 = context.getBean(BeanScope1.class);User user1 = beanScope1.getUser3();System.out.println("BeanScope1" + user1);BeanScope2 beanScope2 = context.getBean(BeanScope2.class);User user2 = beanScope2.getUser3();System.out.println("BeanScope2" + user2);}
}

运行结果: 说明默认为单例

BeanScope1User{id=3, name=‘八戒’}
BeanScope2User{id=3, name=‘八戒’}


2.2、设置作用域

  • @Scope(“prototype”)
@Bean(name = "user3")
@Scope("prototype") // 原型模式,每次请求生成一个对象
public User getUser3() {User user = new User();user.setId(3);user.setName("Java");return user;
}

运行结果:

BeanScope1User{id=3, name=‘八戒’}
BeanScope2User{id=3, name=‘Java’}

  • @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Bean(name = "user3")@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public User getUser3() {User user = new User();user.setId(3);user.setName("Java");return user;}

在这里插入图片描述

运行结果:

BeanScope1User{id=3, name=‘八戒’}
BeanScope2User{id=3, name=‘Java’}


2.3、singleton

官方说明:(Default) Scopes a single bean definition to a single object instance for each
Spring IoC container.
描述:该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过
applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀
个对象。
场景:通常无状态的Bean使⽤该作⽤域。无状态表示Bean对象的属性状态不需要更新
备注:Spring默认选择该作⽤域


2.4、prototype

官方说明:Scopes a single bean definition to any number of object instances.
描述:每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过
applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注⼊)都是新的
对象实例。
场景:通常有状态的Bean使⽤该作⽤域


2.5、request

官方说明:Scopes a single bean definition to the lifecycle of a single HTTP request. That
is, each HTTP request has its own instance of a bean created off the back of a single
bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
描述:每次http请求会创建新的Bean实例,类似于prototype
场景:⼀次http的请求和响应的共享Bean
备注:限定SpringMVC中使⽤


2.6、session

官方说明:Scopes a single bean definition to the lifecycle of an HTTP Session. Only
valid in the context of a web-aware Spring ApplicationContext.
描述:在⼀个http session中,定义⼀个Bean实例
场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
备注:限定SpringMVC中使⽤


2.7、application(了解)

官方说明:Scopes a single bean definition to the lifecycle of a ServletContext. Only valid
in the context of a web-aware Spring ApplicationContext.
描述:在⼀个http servlet Context中,定义⼀个Bean实例
场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
备注:限定SpringMVC中使⽤


2.8、websocket(了解)

官方说明:Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in
the context of a web-aware Spring ApplicationContext.
描述:在⼀个HTTP WebSocket的生命周期中,定义⼀个Bean实例
场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息
头。第⼀次初始化后,直到WebSocket结束都是同⼀个Bean。
备注:限定Spring WebSocket中使⽤


2.9、单例作⽤域(singleton) 和 全局作⽤域(application) 的区别

项目类型不同: singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域;
作用容器不同: singleton 作⽤于 IoC 的容器,而 application 作⽤于 Servlet 容器


3、Bean 原理分析

Bean 执行流程(Spring 执行流程):启动 Spring 容器 -> 实例化 Bean(分配内存空间,从无到有)
-> Bean 注册到 Spring 中(存操作) -> 将 Bean 装配到需要的类中(取操作)

在这里插入图片描述


三、Bean 生命周期

1、5 个流程

所谓的生命周期指的是⼀个对象从诞生到销毁的整个生命过程,我们把这个过程就叫做⼀个对象的生命周期。

Bean 的生命周期分为以下 5 大部分:

  1. 实例化 Bean(为 Bean 分配内存空间)

  2. 设置属性(Bean 注⼊和装配)

  3. Bean 初始化

    • 实现了各种 Aware 通知的方法,如 BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法;

    • 执行 BeanPostProcessor 初始化前置方法;

    • 执行构造方法,有两种执行方式:

    • 注解: @PostConstruct 初始化方法,依赖注⼊操作之后被执行;

    • xml:执行自己指定的 init-method 方法(如果有指定的话);<bean init-method=""></bean>

    • 如果两个都设置了,先执行注解,

    • 执行 BeanPostProcessor 初始化后置方法

  4. 使⽤ Bean

  5. 销毁 Bean

    • 销毁容器的各种方法,如 @PreDestroy (注解)、DisposableBean 接口方法、destroy-method (xml)

2、实例化和初始化的区别

实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可⼈⼯⼲预和修改;而初始化是给
开发者提供的,可以在实例化之后,类加载完成之前进行⾃定义“事件”处理


3、生命流程的“故事”

Bean 的生命流程看似繁琐,但咱们可以以生活中的场景来理解它,比如我们现在需要买一栋房子,那么我们的流程是这样的:

  1. 先买房(实例化,从无到有);

  2. 装修(设置属性);

  3. 买家电,如洗衣机、冰箱、电视、空调等([各种]初始化);

  4. 入住(使用 Bean);

  5. 卖出去(Bean 销毁)


4、生命周期演示

——创建类 BeanLifeComponent:

// @Component // 在 xml 中 使用 bean 标签注入了,不需要类注解
public class BeanLifeComponent implements BeanNameAware {@PostConstructpublic void postConstruct() {System.out.println("执⾏ @PostConstruct()");}public void init() {System.out.println("执⾏ init-method");}public void use() {System.out.println("使用 bean");}@PreDestroypublic void preDestroy() {System.out.println("执⾏ @PreDestroy");}public void setBeanName(String s) {System.out.println("执⾏了 Aware 通知");}
}

——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"xmlns:content="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><content:component-scan base-package="com.beans"></content:component-scan><bean id="beanLifeComponent" class="com.beans.BeanLifeComponent" init-method="init"></bean></beans>

——调⽤类:

public class App {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");BeanLifeComponent beanLifeComponent = context.getBean("beanLifeComponent", BeanLifeComponent.class);beanLifeComponent.use();context.destroy(); // 注销上下文对象}
}

运行结果:

执行了 Aware 通知
执行 @PostConstruct()
执行 init-method
使用 bean
执行 @PreDestroy


2、为什么先设置属性再初始化

也就是 步骤 2 和 步骤 3 的顺序,能不能打个颠倒?

@Service
public class UserService {public UserService(){System.out.println("调⽤ User Service 构造⽅法");}public void sayHi(){System.out.println("User Service SayHi.");}
}@Controller
public class UserController {@Resourceprivate UserService userService;@PostConstructpublic void postConstruct() {userService.sayHi(); // 执行构造方法时使用 会空指针System.out.println("执⾏ User Controller 构造⽅法");}
}

相关文章:

Spring | Bean 作用域和生命周期

一、通过一个案例来看 Bean 作用域的问题 Spring 是用来读取和存储 Bean&#xff0c;因此在 Spring 中 Bean 是最核心的操作资源&#xff0c;所以接下来我们深入学习⼀下 Bean 对象 假设现在有⼀个公共的 Bean&#xff0c;提供给 A 用户和 B 用户使用&#xff0c;然而在使用的…...

培训(c++题解)

题目描述 某培训机构的学员有如下信息&#xff1a; 姓名&#xff08;字符串&#xff09;年龄&#xff08;周岁&#xff0c;整数&#xff09;去年 NOIP 成绩&#xff08;整数&#xff0c;且保证是 5 的倍数&#xff09; 经过为期一年的培训&#xff0c;所有同学的成绩都有所提…...

ansible-playbook编写 lnmp 剧本

ansible-playbook编写 lnmp 剧本 vim /opt/lnmp/lnmp.yaml执行剧本 ansible-playbook lnmp.yaml...

需求太多处理不过来?MoSCoW模型帮你

一、MoSCoW模型是什么 MoSCoW模型 是在项目管理、软件开发中使用的一种排序优先级的方法&#xff0c;以便开发人员、产品经理、客户对每个需求交付的重要性达成共识。 MoSCoW是一个首字母缩略词&#xff0c;代表&#xff1a; M&#xff08;Must have&#xff09;&#xff1a…...

Vue 3:玩一下web前端技术(六)

前言 本章内容为VUE请求后端技术与相关技术讨论。 上一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;五&#xff09;_Lion King的博客-CSDN博客 下一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;七&#xff09;_Lio…...

【点云处理教程】00计算机视觉的Open3D简介

一、说明 Open3D 是一个开源库&#xff0c;使开发人员能够处理 3D 数据。它提供了一组用于 3D 数据处理、可视化和机器学习任务的工具。该库支持各种数据格式&#xff0c;例如 .ply、.obj、.stl 和 .xyz&#xff0c;并允许用户创建自定义数据结构并在程序中访问它们。 Open3D 广…...

Windows10系统还原操作

哈喽&#xff0c;大家好&#xff0c;我是雷工&#xff01; 复制了下虚拟机的Win10系统&#xff0c;但其中有一些软件&#xff0c;想实现类似手机的格式化出厂操作&#xff0c;下面记录Windows10系统的还原操作。 一、系统环境&#xff1a; 虚拟机内的Windows10&#xff0c;64…...

Django学习笔记-模板(Template)基础

使用模块可以很方便的执行一些数据操作&#xff0c;然后根据传入的数据直接在模板html文件中进行处理。 1.Django中的模板配置 Django的模板引擎在sttings.py文件中&#xff1a; TEMPLATES [{# 模板引擎&#xff0c;默认为django模板BACKEND: django.template.backends.dja…...

使用 NVM(Node Version Manager)管理 Node.js 版本

使用 NVM&#xff08;Node Version Manager&#xff09;管理 Node.js 版本 步骤一&#xff1a;安装 NVM NVM 是一个用于安装和管理不同版本的 Node.js 的工具。首先&#xff0c;你需要确保你的系统上已经安装了 NVM。可以通过以下命令检查 NVM 是否已经安装&#xff1a; nvm …...

(文章复现)梯级水光互补系统最大化可消纳电量期望短期优化调度模型matlab代码

参考文献&#xff1a; [1]罗彬,陈永灿,刘昭伟等.梯级水光互补系统最大化可消纳电量期望短期优化调度模型[J].电力系统自动化,2023,47(10):66-75. 1.基本原理 1.1 目标函数 考虑光伏出力的不确定性&#xff0c;以梯级水光互补系统的可消纳电量期望最大为目标&#xff0c;函数…...

tinkerCAD案例:24. Ruler - Measuring Lengths 标尺 -量勺

tinkerCAD案例&#xff1a;24. Ruler - Measuring Lengths 标尺 - 测量长度 Project Overview: 项目概况&#xff1a; A machine shop, where any idea can become a reality, can cost millions and million of dollars. Still, the most important tool in the shop is the…...

linux系统编程重点复习--线程同步

目录 复习目标&#xff1a; 1 互斥锁 1.1互斥锁的使用步骤 1.2 练习 1.3 死锁 2 读写锁 3 条件变量 4 信号量 复习目标&#xff1a; 熟练掌握互斥量的使用说出什么叫死锁以及解决方案熟练掌握读写锁的使用熟练掌握条件变量的使用理解条件变量实现的生产消费者模型理解…...

【Docker 学习笔记】Windows Docker Desktop 安装

文章目录 一、前言二、Windows Docker 安装1. 基于Hyper-V后端和Windows容器的安装2. 基于WSL2后端的安装&#xff08;推荐&#xff09;3. 安装Docker Desktop on Windows4. 启动并验证Docker Desktop 一、前言 Docker并非是一个通用的容器工具&#xff0c;它依赖于已存在并运…...

getInputStream has already been called for this request 问题记录

问题背景 HttpServletRequest.getReader() HttpServletRequest.getInputStream() 不能在过滤器中读取一次二进制流&#xff08;字符流&#xff09;&#xff0c;又在另外一个Servlet中读取一次&#xff0c;即一个InputSteam(BufferedReader)对象在被读取完成后&#xff0c;将无…...

日撸代码300行:第60天(小结)

1、自己对于这个专栏的代码抄写也是断断续续&#xff0c;由于种种原因上次在第54天没坚持下来&#xff0c;这次继续希望能抄完。 2、现在代码的阅读和理解能力明显比刚开始抄代码的时候强了不少。感觉坚持到现在收获还是不小。现在基本上来说仔细想一下都能够理清楚代码的意思。…...

python和java哪个更有前景,python和java哪个更有前途

大家好&#xff0c;小编为大家解答python和java哪个好学,零基础的问题。很多人还不知道python和java哪个更容易入门&#xff0c;现在让我们一起来看看吧&#xff01; 进入编程行业是很多人的梦想&#xff0c;现在越来越多的人都想要通过培训的方式进入IT行业中&#xff0c;但是…...

LeetCode_11. 盛最多水的容器

题目描述 11. 盛最多水的容器 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/container-with-most-water/ 思路分析 这题就是典型的是一道很经典的面试题&#xff0c;最优的解法是双指针&#xff0c;但很多人在第一次看到这题的时候很难想到用双指针来…...

【Android】APP电量优化学习笔记

电量优化原因 电量优化在 Android 开发中非常重要&#xff0c;原因如下&#xff1a; 用户体验&#xff1a; 电池续航时间是用户在使用移动设备时非常关注的因素之一。通过进行电量优化&#xff0c;可以延长设备的电池寿命&#xff0c;使用户能够更长时间地使用设备而不必频繁…...

【微信小程序创作之路】- 小程序事件绑定、动态提示Toast、对话框 Modal

【微信小程序创作之路】- 小程序事件绑定、动态提示Toast、对话框 Modal 第六章 小程序事件绑定、动态提示Toast、对话框 Modal 文章目录 【微信小程序创作之路】- 小程序事件绑定、动态提示Toast、对话框 Modal前言一、事件是什么&#xff1f;二、小程序中常用事件三、事件传…...

MVC与MVVM模式的区别

一、MVC Model&#xff08;模型&#xff09;&#xff1a;用于处理应用程序数据逻辑&#xff0c;负责在数据库中存取数据。处理数据的crud View&#xff08;视图&#xff09;&#xff1a;处理数据显示的部分。通常视图是依据模型数据创建的。 Controller&#xff08;控制器&…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

XCTF-web-easyupload

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

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; 左…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...