【Spring Boot】Spring 魔法世界:Bean 作用域与生命周期的奇妙之旅

前言

???本期讲解关于spring原理Bean的相关知识介绍~~~

??感兴趣的小伙伴看一看小编主页:-CSDN博客

?? 你的点赞就是小编不断更新的最大动力

??那么废话不多说直接开整吧~~

目录

???1.Bean的作用域

??1.1概念

??1.2Bean的作用域

??1.3代码演示

???2.Bean的生命周期

??2.1概念以及分类

??2.2代码演示

??2.3原码阅读

2.3.1解析Bean类

2.3.2实例化前处理

2.3.3创建Bean的实例

2.3.4初始化Bean

???3.总结

**??**1.Bean的作用域

??1.1概念

在Spring IoC&DI阶段, 我们学习了Spring是如何帮助我们管理对象的.

1. 通过 @Controller , @Service , @Repository , @Component , @Configuration ,@Bean 来声明Bean对象.

2. 通过 ApplicationContext 或者 BeanFactory 来获取对象

3. 通过 @Autowired , Setter 法或者构造法等来为应程序注所依赖的Bean对象

如下代码所示:

首先我们在model层定义一个实体类:

public class Dog {public String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}

然后我们在config层通过@Bean将对象交给spring帮我们进行管理:

@Configuration
public class DogBeanConfig {//使用@Bean将对象交给spring进行管理@Beanpublic Dog dog(){Dog dog = new Dog();dog.setName("wangcai");return dog;}
}

那么在后面我们可以从spring容器中获取得到这里对象;

@SpringBootTest
class SpringPrincipleApplicationTests {@AutowiredApplicationContext context;@Testvoid contextLoads() {//单列模式Dog dog = context.getBean("dog", Dog.class);System.out.println(dog);}}

当然这小编是在test包中进行测试使用的,所以需要注入applicationcontext类,获取spring上下文(获取spring容器),再拿到这里对象;

此时我们再次拿对象,然后进行两次对象获取打印对应的地址发现:

地址是一样的, 说明每次从Spring容器中取出来的对象都是同个.这也是"单例模式"

单例模式: 确保个类只有个实例,多次创建也不会创建出多个实例
默认情况下, Spring容器中的bean都是单例的, 这种为模式, 我们就称之为Bean的作域

所以bean的作用域概念就是:

Bean的作用域是指在spring框架中一种行为模式

单例作用域表示全局只有一份,他是全局共享的,若进行了修改,那么再次获取次对象的某个属性就是被修改过后的属性;

但是如何再次访问时,如何重新创建一个对象呢,那么这就是其他的作用域了;

??1.2Bean的作用域

Bean的作用域分为6种,如下所示:

1. singleton:单例作域

2. prototype:原型作域(多例作域)
3. request:请求作域
4. session:会话作域
5. Application: 全局作域
6. websocket:HTTP WebSocket 作域

这六种作用域的大致作用意义如下表所示:

Bean的6种作用域

singleton

每个Spring IoC容器内同名称的bean只有个实例(单例)(默认

prototype

每次使该bean时会创建新的实例(单例)

request

每个HTTP 请求命周期内, 创建新的实例

session

每个HTTP Session命周期内, 创建新的实例

Application

每个ServletContext命周期内, 创建新的实例

websocket

每个WebSocket命周期内, 创建新的实例

??1.3代码演示

以下就是代码演示:

@Configuration
public class DogBeanConfig {//使用@Bean将对象交给spring进行管理@Beanpublic Dog dog(){Dog dog = new Dog();dog.setName("wangcai");return dog;}@Bean@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)public Dog singletonDog(){Dog dog =new Dog();return dog;}//原型作用域@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public Dog prototypeDog(){Dog dog =new Dog();return dog;}//请求作用域@Bean@RequestScopepublic Dog requestDog(){Dog dog =new Dog();return dog;}//会话作用域@Bean@SessionScopepublic Dog sessionDog(){Dog dog =new Dog();return dog;}@Bean@ApplicationScopepublic Dog applicationDog(){Dog dog =new Dog();return dog;}
}

其中单列作用域,与多列作用域(原型作用域)使用@scope注解,其余作用域使用对应名字的注解,内部等于

@Scope(value =WebApplicationContext.SCOPE_REQUEST, proxyMode =
ScopedProxyMode.TARGET_CLASS)

其中黄色标注部分是可以进行对应作用域的更改的;其中 proxyMode为spring bean设置代理,表示bean是基于CGLIB进行动态代理的;

测试代码如下所示:

@RestController
@RequestMapping("/test")
public class DogBeanController {@Autowiredprivate ApplicationContext context;@Autowiredprivate Dog singletonDog;@Autowiredprivate Dog prototypeDog;@Autowiredprivate Dog requestDog;@Autowiredprivate Dog sessionDog;@Autowiredprivate Dog applicationDog;/*** 通过比较不同作用域,spring容器启动直接注入,后不会进行改变(原型作用域)* @return*/@RequestMapping("/single")public String single(){//从context获取对象Dog contexDog = context.getBean("singletonDog",Dog.class);return "contextDog:"+contexDog +",autowiredDog:"+singletonDog;}@RequestMapping("/prototype")public String prototype(){//从context获取对象Dog contexDog = context.getBean("prototypeDog",Dog.class);return "contextDog:"+contexDog +",<br/> autowiredDog:"+prototypeDog;}@RequestMapping("/request")public String request(){//从context获取对象Dog contexDog = context.getBean("requestDog",Dog.class);return "contextDog:"+contexDog +",<br/> autowiredDog:"+requestDog;}@RequestMapping("/session")public String session(){//从context获取对象Dog contexDog = context.getBean("sessionDog",Dog.class);return "contextDog:"+contexDog +",<br/> autowiredDog:"+sessionDog;}@RequestMapping("/application")public String application(){//从context获取对象Dog contexDog = context.getBean("applicationDog",Dog.class);return "contextDog:"+contexDog +",<br/> autowiredDog:"+applicationDog;}
}

这里需要注意的是,autowired直接注入是在spring容器启动时就进行注入,Applicationcontext获取容器注入是每次请求才注入对象;

所以具体的请求情况如下所示:

对于单列来说,对象是共享的,所以注入的对象地址两个都是一样的;

对于原型作用域:

由于每次请求使用bean时都会创建新的实例,所以多次请求会进行变化,spring启动时就已经注入,所以不会进行变化(除非重启服务);

对于请求作用域:

因为每次请求都会重新创建实例,所以不断刷新后对象地址就会进行改变;

对于会话作用域:

对于会话作用域,范围比请求作用域更加广泛,在一个浏览器上算是一个会话,如果要进行改变对象地址,就得重新开启一个会话,那么可以使用两个浏览器进行url的请求访问,那么此时不同浏览器的对象地址就是不一样的;

最后一个应用作用域,小编就不再进行演示了,结果和单例一样;

Application scope就是对于整个web容器来说, bean的作域是ServletContext级别的. 这个和
singleton有点类似,区别在于: Application scope是ServletContext的单例, singleton是个
ApplicationContext的单例. 在个web容器中ApplicationContext可以有多个

**??**2.Bean的生命周期

??2.1概念以及分类

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

Bean 的命周期分为以下5个部分:
1. 实例化(为Bean分配内存空间)

2. 属性赋值(Bean注和装配, 如 @AutoWired )

3. 初始化
a. 执各种通知, 如 BeanNameAware , BeanFactoryAware ,ApplicationContextAware 的接口方法.
b. 执初始化法
xml定义 init-method
使注解的式 @PostConstruct
执初始化后置法( BeanPostProcessor )

4. 使Bean

5. 销毁Bean

??2.2代码演示

代码如下所示:

@Component
public class BeanLifeComponent implements BeanNameAware {public Dog singletonDog;public BeanLifeComponent() {System.out.println("执行构造函数...");}//属性注入@Autowiredpublic void setSingletonDog(Dog singletonDog) {this.singletonDog = singletonDog;System.out.println("执行setSingletonDog....");}//执行各种通知@Overridepublic void setBeanName(String name) {System.out.println("setBeanName: "+name);}//初始化方法@PostConstructpublic void init(){System.out.println("执行PostConstruct...");}//使用beanpublic void use(){System.out.println("执行use方法....");}//销毁bean@PreDestroypublic void destroy(){System.out.println("执行destroy方法");}
}

对应就是,实例化分配内存,然后属性注入,在进行初始化(各种通知,以及通过注解初始化方法,最后使用bean,以及销毁bean)

日志打印如下:

??2.3原码阅读

首先点击进入AbstractAutowireCapableBeanFactory类查看源码内容:

找到AbstractAutowireCapableBeanFactory类里createBean方法的实现

此方法主要在创建过程中,会先进行一些准备工作,比如解析 Bean 的类,还会尝试在实例化之前进行一些处理,若有合适的处理器处理并返回了 Bean 实例,就直接返回;若没有,则调用doCreateBean方法实际创建 Bean 实例。

2.3.1解析Bean类
 Class<?> resolvedClass = this.resolveBeanClass(mbd, beanName, new Class[0]);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);

this.resolveBeanClass(mbd, beanName, new Class[0]):尝试解析 Bean 定义对应的类。若解析成功,且 Bean 定义里没有直接设置类,同时 Bean 类名不为空,就创建一个新的 RootBeanDefinition 对象 mbdToUse,并把解析后的类设置进去。
qwqw

2.3.2实例化前处理
       try {beanInstance = this.resolveBeforeInstantiation(beanName, mbdToUse);if (beanInstance != null) {return beanInstance;}

这里的resolveBeforeInstantiation会调用后至处理器的方法,在执行后,判断如果该实例为非空的,那么直接返回这个实例,不再执行下面实例化的创建过程;

2.3.3创建Bean的实例

在上述解析和实例化处理后,就开始执行实例的创建工作了,主要的方法就是doBeanCreate;

        try {beanInstance = this.doCreateBean(beanName, mbdToUse, args);if (this.logger.isTraceEnabled()) {this.logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;

this.doCreateBean(beanName, mbdToUse, args):调用该方法实际创建 Bean 实例,这个方法包含了实例化、属性注入、初始化等一系列操作。

点开doCreateBean方法;

在此方法中:

这三个法与三个命周期阶段对应
1. createBeanInstance() -> 实例化
2. populateBean() -> 属性赋值
3. initializeBean() -> 初始化

点击进入initializeBean();

2.3.4初始化Bean

initializeBean 方法的主要作用是对已经实例化的 Bean 进行初始化操作,包括调用 Aware 接口方法、应用 BeanPostProcessor 前置处理、调用初始化方法以及应用 BeanPostProcessor 后置处理。这里可以对照英文单词看看;

其中初始化方法如下:

invokeAwareMethods 方法用于检查 Bean 是否实现了特定的 Aware 接口,若实现了就判断是否实现了特定的子接口;例如实现了BeanNameAware接口,调用相应的 setter 方法,将相关信息注入到 Bean 中。

大致的思维导图就是:

**??**3.总结

本期主要讲解了Spring原理中的Bean的作用域以及生命周期,通过概念以及相关代码进行演示,最后深入源码讲解Bean的生命周期的实现过程;

???~~~~最后希望与诸君共勉,共同进步!!!


???以上就是本期内容了, 感兴趣的话,就关注小编吧。

???期待你的关注~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/17842.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用 Python 实现 DeepSeek R1 本地化部署

DeepSeek R1 以其出色的表现脱颖而出&#xff0c;不少朋友想将其本地化部署&#xff0c;网上基于 ollama 的部署方式有很多&#xff0c;但今天我要带你领略一种全新的方法 —— 使用 Python 实现 DeepSeek R1 本地化部署&#xff0c;让你轻松掌握&#xff0c;打造属于自己的 AI…

软考-系统架构设计师(月更版)

1.需求管理的主要活动包括变更控制&#xff0c;版本控制&#xff0c;需求跟踪&#xff0c;需求状态跟踪 需求跟踪是单个需求和其他系统元素之间的依赖关系和逻辑联系建跟踪&#xff0c; 这些元素包括各种类型的需求、业务规则、系统架构和构件、源代码、测试用例&#xff0c;以…

IOTDB安装部署

IOTDB一般用于工业互联网&#xff0c;至于具体的介绍请自行搜索 1.环境准备 安装前需要保证设备上配有 JDK>1.8 的运行环境&#xff0c;并配置好 JAVA_HOME 环境变量。 设置最大文件打开数为 65535。 关闭防火墙 systemctl stop firewalld.service systemctl disable …

分享 UniApp 实现列表长按删除功能

在移动应用开发中&#xff0c;列表是常见的展示形式&#xff0c;而长按删除列表项也是一个实用且常见的交互功能。今天就来和大家分享如何在 UniApp 中实现列表的长按删除功能&#xff0c;同时附上详细的代码。 效果预览 通过代码实现后&#xff0c;我们将得到一个带有红色边…

leetcode 2684. 矩阵中移动的最大次数

题目如下 数据范围 本题使用常规动态规划就行&#xff0c;不过要注意由于有三个转移的方向&#xff0c;所以我们对dp数组的遍历应该是从上到下 从左到右即按列优先遍历。通过代码 class Solution { public:int maxMoves(vector<vector<int>>& grid) {int …

深入剖析 Burp Suite:Web 应用安全测试利器

目录 前言 一、Burp Suite 简介 二、功能组件详解 三、使用场景 四、安装与使用步骤 安装步骤 使用步骤 五、总结 前言 在网络安全的复杂版图中&#xff0c;Burp Suite 宛如一颗璀璨的明珠&#xff0c;以其强大的功能和广泛的适用性&#xff0c;成为众多安全从业者不可…

linux内网部署deepseek大模型(ollama+anythingllm)

一、安装ollama 来源&#xff1a;ollama/docs/linux.md at main ollama/ollama GitHub 1.下载安装包 ollama下载链接&#xff1a;https://ollama.com/download/ollama-linux-amd64.tgz&#xff0c;下载后放在用户目录下 2.运行脚本 #!/bin/sh # This script installs Olla…

【学术投稿-第四届智能电网和绿色能源国际学术会议(ICSGGE 2025)】CSS基本选择器详解:掌握基础,轻松布局网页

可线上 官网&#xff1a;www.icsgge.org 时间&#xff1a;2025年2月28-3月2日 目录 前言 一、基本选择器简介 1. 元素选择器&#xff08;Type Selector&#xff09; 基本语法 示例 注意事项 2. 类选择器&#xff08;Class Selector&#xff09; 基本语法 示例 注意…

聊聊 IP 地址和端口号的区别

在计算机网络中&#xff0c;两个基本概念对于理解设备如何通过网络进行通信至关重要。IP 地址和端口号是 TCP/IP 的典型特征&#xff0c;其定义如下&#xff1a;IP 地址是分配给连接到网络的每台机器的唯一地址&#xff0c;用于定位机器并与其通信。相反&#xff0c;端口号用于…

【个人开发】cuda12.6安装vllm安装实践【内含踩坑经验】

1. 背景 vLLM是一个快速且易于使用的LLM推理和服务库。企业级应用比较普遍&#xff0c;尝试安装相关环境&#xff0c;尝试使用。 2. 环境 模块版本python3.10CUDA12.6torch2.5.1xformers0.0.28.post3flash_attn2.7.4vllm0.6.4.post1 2.1 安装flash_attn 具体选择什么版本&…

问界M8细节曝光,L3自动驾驶有了!

文 | AUTO芯球 作者 | 雷慢 太惊喜了&#xff0c; 问界M8近距离实拍曝光了&#xff0c; 我看了一圈&#xff0c; 给大家扒出几个炸裂的信息&#xff0c; 注意看侧身这一堆传感器&#xff0c; 这可不是什么普通摄像头&#xff0c; 这一片传感器和和尊界S800那套一模一样&a…

idea打开springboot项目打不开文件夹 一直loading

点击file - InvalidateCaches 全选 后点击 Invalidate and Restart

early bird inject

基本原理 本质是利用windows系统的apc机制&#xff0c;以及涉及到windows进程启动的流程. 因为线程初始化阶段LdrInitializeThunk函数会调用NtTestAlert函数,这个函数执行后,所有apc队列中的例程都会执行.因此我们在主线程初始化之前向主线程的apc队列中加入恶意代码即可实现…

Flutter 实现 iOS 小组件与主 App 的通信

前言 在 Flutter 项目中实现 iOS 小组件与主 App 的通信&#xff0c;主要依赖于 App Groups 技术&#xff0c;以下是具体实现步骤和注意事项。 配置 App Groups 在 Xcode中为 Flutter 主应用&#xff08;Runner&#xff09;和 Widget Extension 配置相同的App Group 标识符…

【Unity Shader编程】之顶点着色器

来一张AI提供的资料 在shader编程中&#xff0c;定义的结构体&#xff0c;有些是会被自动赋值&#xff0c;有些是必须要手动赋值的&#xff0c;这就涉及到了语义&#xff0c; 例如 struct appdata{float4 vertex : POSITION;float vertex2;float2 uv : TEXCOORD0;};结构体里面定…

数据结构与算法-栈

参考学习&#xff1a;B站-逊哥带你学编程 栈的定义与实现 补充&#xff1a; 栈是限制插入和删除操作只能在一个位置进行的表&#xff0c;该位置是表的末端&#xff0c;叫作栈顶(top)。 对栈的基本操作有进栈(push)和出栈(Pop)&#xff0c;前者相当于插入后者则是删除最后插入…

嵌入式硬件篇---OpenMV的硬件流和软件流

文章目录 前言一、硬件流控制&#xff08;Hardware Flow Control&#xff09;1. 基本原理RTSCTS 2. OpenMV中的实现• 硬件要求• 代码配置• 工作流程 二、软件流控制&#xff08;Software Flow Control&#xff09;1. 基本原理XONXOFF 2. OpenMV中的实现• 代码配置• 工作流…

MySQL Workbench菜单汉化为中文

默认情况下&#xff0c;安装完成的MySQL Workbench的菜单为英文&#xff0c;今天介绍一个简单易操作的方法&#xff0c;将MySQL Workbench菜单汉化为中文。 一、查找MySQL Workbench菜单标记文件main_menu.xml 1. 默认情况下&#xff0c;MySQL Workbench的安装路径为&#xff…

C++从入门到实战(四)C++引用与inline,nullptr

C从入门到实战&#xff08;四&#xff09;C引用与inline&#xff0c;nullptr 前言一、C 引用&#xff08;一&#xff09;什么是引用&#xff08;二&#xff09;引用的特点&#xff08;三&#xff09;引用作为函数参数&#xff08;四&#xff09;引用作为函数返回值&#xff08;…

【C/C++算法】从浅到深学习--- 二分查找(图文兼备 + 源码详解)

绪论&#xff1a;冲击蓝桥杯一起加油&#xff01;&#xff01; 每日激励&#xff1a;“不设限和自我肯定的心态&#xff1a;I can do all things。 — Stephen Curry” 绪论​&#xff1a; 本章是算法篇章的第三章二分算法&#xff0c;本章主要是通过题目的形式来进行学习&…