【Spring 】执行流程解析:了解Bean的作用域及生命周期

news2024/11/24 20:37:21

 哈喽,哈喽,大家好~ 我是你们的老朋友:保护小周ღ  


今天给大家带来的是 Spring 项目的执行流程解析 Bean 对象的6 种作用域以及生命周期本文将为大家讲解,一起来看看叭~


本期收录于博主的专栏:JavaEE_保护小周ღ的博客-CSDN博客

适用于编程初学者,感兴趣的朋友们可以订阅,查看其它 “JavaEE基础知识”。

更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘


一、Spring 的执行流程 

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

 Spring 注解相关操作感兴趣得老铁可以阅读博主得另一篇博客:

【Spring】使用注解读取和存储Bean对象_保护小周ღ的博客-CSDN博客


二、Bean 的作用域

Spring 是一个包含众多工具方法的 IoC 容器,也可以认为 Spring 就是用来读取和存储 Bean 对象的,那么 Bean 对象就是Spring 中一个至关重要的角色。

2.1 通过案例来观察 Spring 中Bean 对象默认的作用域

设计一个公共的 Student 类的Bean对象,注册到 Spring 容器中,提供给  A用户 和 B用户使用,A用户不讲武德悄悄地修改 Student 类型Bean 对象的数据,B 用户在使用的 Bean 对象的时候发现数据已经被篡改了,相当的恼火~

Student 类如下:

@Data
public class Student {
    private int id; // 学号
    private String name; // 姓名
    private int age; // 年龄
    private String sex; // 性别
}

给大家介绍一个第三方的框架: lombok 

"Lombok"是一个在Java开发中非常常用的开源框架。它的目标是通过自动化生成Java代码的样板代码来简化开发过程,减少冗余代码,并提高代码的可读性。

Lombok提供了一系列的注解,例如@Data、@Getter、@Setter、@NoArgsConstructor等,通过在类上添加这些注解,可以自动生成对应的getter、setter、构造函数等方法。这样可以大大简化Java类的编写,提高开发效率

要使用Lombok,您需要在项目中添加Lombok的依赖,并在IDE中安装Lombok插件,以便在编译和运行时正确处理Lombok的注解。

 Lombox 依赖:

 <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.24</version>
      <scope>provided</scope>
 </dependency>

博主使用 @Data 注解就来源于 lombox ,@Data包含了 @Setter 和 @Getter  注解,也就是自动给Student 类创建 set 和 get 方法。


公共的Bean 对象,注入到 Spring 中:

@Component
public class Users {
    //使用方法注解将 学生张三注入到 Spring 中
    @Bean
    public Student getStu() {
        Student stu = new Student();
        stu.setId(123456);
        stu.setName("张三");
        stu.setAge(18);
        stu.setSex("男");
        return stu;
    }
}

用户A 从Spring 中获取 stu Bean 对象,并进行修改:

@Controller
public class UserA {

    //从Spring 中获取Bean 对象
    @Resource //@Autowired
    private Student stu;

    public Student getStu() {
        System.out.println("用户A获取原Bean对象"+ '\n'+ stu.toString());

        //对原Bean 的数据进行修改
        stu.setName("李四");
        stu.setSex("女");
        return stu;
    }
}

用户B 从Spring 中获取 stu Bean 对象,进行使用(打印):

@Controller
public class UserB {
    //从Spring 中获取Bean 对象
    @Resource //@Autowired
    private Student stu;

    public Student getStu() {
        System.out.println("用户B获取被用户A修改过的 Bean :" + '\n' + stu.toString());
        return stu;
    }
}

启动类中获取用户A 和 用户B 对象,调用getStu() 方法,获取 stu Bean对象:

public class App {
    public static void main(String[] args) {
        //1. 获取Spring 上下文对象,里面管理着 bean 对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        //2. 获取指定的 Bean 对象
        UserA userA = context.getBean("userA", UserA.class);
        Student stuA = userA.getStu();
        System.out.println("用户A:" + stuA.toString());


        UserB userB = context.getBean("userB", UserB.class);
        Student stuB = userB.getStu();
        System.out.println("用户B:"+ stuB.toString());
    }
}


案例分析:

以上操作的的执行结果的原因 Bean 默认情况下是单例模式(singleton:一个类只有一个实例),也就是说用户 A 和 用户 B 操作的都是同一个Bean 对象,所以用户A 修改了唯一的 Bean , 用户B 获取到的 Bean 就是被修改后的了。

在Spring 中Bean 的作用域默认是singleton 单例模式~


2.2 Bean 对象的 6 种作用域

限定程序中变量的可用范围叫做作用域,Bean 对象的作用域指的是在 Spring 整个生命周期中的某种行为模式,例如:单例作用域(singleton)的意思就是在整个 Spring 中只有一份实例,且全局共享,当其他人修改了这个唯一的一份实例后,另一个人再获取到的实例,就是被修改后的值了。

Spring框架为Bean对象提供了多种作用域选项。以下是Spring框架中常见的6种Bean对象作用域:

  1. Singleton(单例):在整个应用程序中只存在一个Bean实例。每次请求都将返回相同的实例。获取Bean(通过applicationContext.getBean等方法获取),装配Bean(即通过@Autowired,@Resource)

  2. Prototype(原型):每次请求将创建一个新的Bean实例。每次请求都返回不同的实例。获取Bean(通过applicationContext.getBean等方法获取),装配Bean(即通过@Autowired,@Resource)

  3. Request(请求):在每次HTTP请求中,都会创建一个新的Bean实例。每个HTTP请求都返回不同的实例。

  4. Session(会话:在用户会话期间,都会创建一个新的Bean实例。对于同一用户的后续请求,将返回相同的实例。

  5. Global Session全局会话:在整个应用程序的全局会话期间,都会创建一个新的Bean实例。对于同一全局会话的后续请求,将返回相同的实例。

  6. Application应用程序:在整个Web应用程序的生命周期内,都会创建一个新的Bean实例。每个Web应用程序都返回相同的实例。

注意:作用域的使用取决于具体的应用场景和需求。Spring框架还支持自定义作用域,普通的Spring 项目只支持 singleton (单例),prototype (多例),剩下的4种状态是在 SpringMVC项目中可以设置。


2.3 给Bean 对象设置作用域

使用 @Scope 标签就可以用来声明 Bean 的作用域, 如以下代码所示:

@Component
public class Users {
    //声明为多例模式,每此请求都会实例一个新的Bean对象
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 
    //使用方法注解将 学生张三注入到 Spring 中
    @Bean(name = "stu") //给 Bean 对象起一个名字
    public Student getStu() {
        Student stu = new Student();
        stu.setId(123456);
        stu.setName("张三");
        stu.setAge(18);
        stu.setSex("男");
        return stu;
    }
}

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

1. 直接设置值: @Scope("prototype")

英语好的朋友可以直接把单词记住,也是蛮直接方便的昂~

2.使用枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

此时我们给上述例子Student 类型的Bean 对象设置了多例模式,这个时候运行一下看看:


三、Bean 的生命周期

所谓 Bean(对象) 生命周期指的就是一个普通对象从创建到销毁的整个过程,我们把整个过程称之为对象的生命周期。Bean 对象也就是一个普通的实例对象。

Bean 对象的生命周期分为以下五个部分:

1. 实例化: Bean (为 Bean对象分配内存空间(堆区))

2. 设置属性:在实例化后,容器会将配置的属性值或引用注入到Bean对象中,可以通过构造函数、setter方法或字段注入的方式完成。

3. Bean 对象的初始化 :Bean对象的初始化流程通常包括以下几个步骤:

  • 加载Bean定义:首先,容器会读取配置文件或通过注解扫描等方式,获取Bean的定义信息,并将其存储在内存中。

  • 创建Bean实例:根据Bean的定义信息,容器会实例化Bean对象。这可以通过调用默认构造函数或使用工厂方法等方式来完成。

  • 注入依赖:一旦Bean实例创建完成,容器会检查Bean定义中所声明的依赖关系,并将相应的依赖注入到Bean实例中。依赖注入可以通过构造函数、setter方法或字段注入方式来实现。

  • 实现Aware接口:如果Bean实现了特定的Aware接口(如BeanNameAware、BeanFactoryAware等),容器会调用相应的回调方法,以便提供一些额外的关于容器的信息。

  • Bean初始化前的处理:在Bean实例初始化之前,容器会调用BeanPostProcessor接口的实现类的postProcessBeforeInitialization()方法,允许开发者在初始化之前进行一些自定义逻辑的处理。

  • 初始化Bean:实例化过程完成后,容器会根据Bean定义中的配置调用初始化方法。这可以是通过在Bean类中标注@PostConstruct注解或实现InitializingBean接口的afterPropertiesSet()方法来完成。

  • Bean初始化后的处理:在Bean实例初始化之后,容器会再次调用BeanPostProcessor接口的实现类的postProcessAfterInitialization()方法,允许开发者在初始化之后进行一些自定义逻辑的处理。

  • 完成Bean的初始化:至此,Bean的初始化流程完成,可以将其添加到容器中,供其他Bean进行依赖注入或其他操作使用。

需要注意的是,以上是一个典型的Bean对象初始化流程,实际应用中可能会有一些差异,例如,使用了代理、继承了父类等情况会导致初始化流程有所不同。同时,不同的容器实现也可能会有一些细微的差异。

4. 使用 Bean : Bean 对象,就是一个普通的实例化的对象,使用都是一样的,看咋类是咋设计的

5. 销毁 Bean :

销毁Bean对象可以通过以下几种方式实现:

  • 使用Destroy方法:可以在Bean类中实现DisposableBean接口,并重写其中的destroy()方法。在容器销毁Bean实例时,会自动调用该方法来销毁Bean对象。
public class MyBean implements DisposableBean {
    // ...
    
    @Override
    public void destroy() {
        // 销毁操作
        // ...
    }
}

  • 使用@PreDestroy注解:可以在Bean类的销毁方法上添加@PreDestroy注解,当容器销毁Bean实例时,会触发该注解标记的方法进行销毁操作。
public class MyBean {

    // ...
    
    @PreDestroy
    public void destroy() {
        // 销毁操作
        // ...
    }
}

  • 配置销毁方法:在配置文件(如XML配置文件)中,可以通过指定destroy-method属性来指定Bean销毁时调用的方法。该方法可以是Bean类中的任意公开方法。
<bean id="myBean" class="com.example.MyBean" destroy-method="destroy">
    <!-- ... -->
</bean>

需要注意的是,销毁Bean对象的时机由容器管理,通常发生在容器关闭时。如果是单例Bean,则在容器关闭或手动调用销毁方法时触发销毁操作。如果是原型或会话作用域(session scoped)的Bean,则需要手动销毁。


执行流程图 :


实例化和初始化的区别:

实例化和属性设置时 Java 级别的系统 “事件”,操作过程不可以人为干预,而初始化是给开发者提供的,可以在实例化之后,类加载完成之前进行自定义 “事件” 处理。

总的来说,实例化是创建Bean实例的过程,而初始化是对已创建的Bean对象进行属性赋值和回调方法调用的过程。实例化是创建Bean对象的基础步骤,而初始化则是在实例创建完成后对Bean对象进行配置和准备的重要步骤。


举个例子形象的描述Bean 对象的生命周期:买房子

1. 实例化:在某市盖了一套毛坯房(从无到有,将字节码转换为内存中的对象,只是分配了内存)

2. 设置属性:(容器会将配置的属性值或引用注入到Bean对象中)。购买装修材料,引入外部资源。

3. 初始化:给房子装修~

  • 各种通知:联系各种装修师傅,水工,电工,瓦工……
  • 初始化的前置工作:装修师傅现场考察毛坯房的环境,制定装修方案。
  • 进行初始化工作:(@PostConstruct 初始化、init-method 初始化),装修师傅进行装修,其中有装修技术好的,有装修技术差的。
  • 初始化的后置工作: 装修完毕之后对房子进行清理。

4. 使用 Bean 对象: 拎包入住~

5. 销毁 Bean 对象: 拆,或者卖掉这套房子~

一定是先设置属性,再进行初始化操作。


好了,到这里,【Spring 】执行流程解析:了解Bean的作用域及生命周期 博主已经分享完了,希望对大家有所帮助,如有不妥之处欢迎批评指正。 

下期预告:【SpringBoot】框架~

感谢每一位观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* 

遇见你,所有的星星都落在我的头上……

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

那些你必须知道的4个matlab小技巧(附最新安装包)

文末福利&#xff1a;MATLAB R2022b软件安装包 MATLAB 简介 01 一、MATLAB简介 数学类科技应用软件包括数值计算&#xff08;Number Crunching&#xff09;型软件和数学分析&#xff08;Math Analysis&#xff09;型软件 数值计算型软件 它们对大批数据具有较强的管理、计…

数据仓库建设-数仓分层

数据仓库能够帮助企业做出更好的决策&#xff0c;提高业务效率和效益&#xff1b;在数据仓库建设时&#xff0c;绕不开的话题就是数仓分层。 一、数据分层的好处 1. 降低数据开发成本 通用的业务逻辑加工好&#xff0c;后续的开发任务可以基于模型快速使用&#xff0c;数据需…

分布式定时任务组件:XXL-JOB

一、GitHub源码地址 https://github.com/xuxueli/xxl-job 二、部署文档 参考&#xff1a;https://blog.csdn.net/qq798867485/article/details/131415408 三、初始化数据库SQL 1、xxl_job_user XxlJob-用户管理 2、xxl_job_group XxlJob-执行器管理 3、xxl…

vue3+vite+pinia+vue-router+ol项目创建及配置

一、vite (一)、定义 vite官网 (二)、操作步骤 注意&#xff1a;两种方式创建目录结构一致 方式一&#xff1a;vite创建脚手架命令&#xff1a; 命令行&#xff1a;npm create vitelatest 然后选择 方式二&#xff1a;命令行直接声明带上vue 二、pinia (一)、定义 定义&#xf…

TortoiseGit 入门指南12:创建标签

前面的文章不止一次的提到过 标签 &#xff08;Tag&#xff09;&#xff0c;我们在《TortoiseGit 入门指南08&#xff1a;浏览引用以及在引用间切换》一文中知道&#xff0c;标签 是一种 引用&#xff1b;还知道每个提交都对应着一个 SHA-1 值&#xff0c;而引用就是 SHA-1 的一…

常见面试题之HashMap

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

jmeter压测过程中,ServerAgent响应异常:Cannot send data to network connection

ServerAgent异常信息&#xff1a; Cannot send data to network connection&#xff08;无法将数据发送到网络连接&#xff09; 原因&#xff1a; linux 防火墙 拦截了当前端口 解决方案&#xff1a; Linux 执行以下命令 /sbin/iptables -I INPUT -p tcp --dport 4445 -j ACC…

高压功率放大器应用场合是什么

高压功率放大器是一种能够将低电压信号转换为高电压输出信号的设备。它通常由前置放大器和功率放大级组成&#xff0c;广泛应用于雷达、医疗、半导体测试和工业自动化等领域。下面安泰电子将介绍高压功率放大器的几个主要应用场合。 一、雷达系统 雷达系统需要产生高频、高功率…

【IDEA大项目依赖分析卡死-解决方案】Processing build files for dependencies analysis...

最近一直在研究一个大型项目&#xff0c;在IDEA里面启动调试的时候&#xff0c;IDEA经常会进行Processing build files for dependencies analysis…&#xff08;处理构建文件进行依赖分析&#xff09;&#xff0c;并且在这个步骤耗时太久甚至直接卡死。经过一些排查找到了解决…

arping命令 ip地址冲突检测 根据ip查mac地址

arping命令介绍 arping 命令主要用来获取ip对应的mac地址&#xff0c;更新本地arp缓存表。平时主要用来探测ip地址是否冲突即同一个网络里&#xff0c;同一个ip不同mac地址的情况。ip地址冲突将导致网络故障。 arping常用命令参数 arping [参数] ip -U 强制更新邻近主机的a…

Android系统开发-入门篇

参见&#xff1a;[视频教程] 写给应用开发的 Android Framework 教程——玩转 AOSP 篇之 Android 系统开发工具推荐 - 掘金 前置条件&#xff1a; android系统源码位于 linux 服务器&#xff0c;ssh 地址假如为&#xff1a;test172.1.10.2本机为windows 1、本机&#xff1a; 下…

leetcode 101.对称二叉树

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;对称二叉树 思路&#xff1a; 这道题和 leetcode 100.相同的树 类似&#xff0c;是上一道的变形题。✨leetcode 100.相同的树 代码链接&#xff1a;【往期文章】leetcode 100.相同的树。这道题把根的左子树和右子树看作两…

ROS:动态参数

目录 一、前言二、概念三、作用四、实际用例4.1需求4.2客户端4.2.1流程4.2.2新建功能包4.2.3添加.cfg文件4.2.4配置 CMakeLists.txt4.2.5编译 4.3服务端(C)4.3.1流程4.3.2vscode配置4.3.3服务器代码实现4.3.4编译配置文件4.3.5执行 4.4服务端(Python)4.4.1流程4.4.2vscode配置4…

Spring核心问题解答

1.谈谈对Spring的理解 Spring是Java EE编程领域的一个轻量级开源框架&#xff0c;该框架由一个叫Rod Johnson的程序员在 2002 年最早提出并随后创建&#xff0c;是为了解决企业级编程开发中的复杂性&#xff0c;实现敏捷开发的应用型框架 。 Spring是一个开源容器框架&#x…

Python+Qt窗体或Django网页支付宝收款码-扫码付款实例

程序示例精选 PythonQt窗体或Django网页支付宝收款码-扫码付款实例 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<PythonQt窗体或Django网页支付宝收款码-扫码付款实例>>编写代…

智安网络|保护数据资产:不同行业下的数据安全建设策略

在当今数字化时代&#xff0c;数据安全已经成为各行各业无法忽视的重要议题。保持良好网络卫生习惯并及时了解不断变化的网络威胁是企业中每个人的责任。企业、政府机构、医疗机构、金融机构以及其他组织和行业都面临着日益复杂和频繁的网络安全威胁。为了有效应对这些威胁&…

22.代理模式

代理模式 二十三种设计模式中的一种&#xff0c;属于结构型模式。它的作用就是通过提供一个代理类&#xff0c;在调用目标方法的时候&#xff0c;不再是直接对目标方法进行调用&#xff0c;而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦…

自定义表单设计:办公协同效率提高新工具!

为了提高工作效率和表格制作效率&#xff0c;低代码开发平台成为广大用户喜爱的主流平台。因为它包含了非常多的功能内容&#xff0c;在快节奏发展的社会环境中可以满足日益增长的业务需求&#xff0c;是助力通信业、医疗、高校、物流等众多行业办公效率提质增效的得力助手。其…

解决MySQL中分页查询时多页有重复数据,实际只有一条数据的问题

0 前言 有一个离奇的BUG&#xff0c;在查询时&#xff0c;第一页跟第二页有一个共同的数据。有的数据却不显示。 后来发现是在SQL排序时没用主键排序。 解决&#xff1a;使用主键排序 以下是我准备的举例&#xff0c;可以自己试试。 1 数据准备 SET NAMES utf8mb4; SET FORE…

JVM系列(7)——java内存模型

一、什么是JMM 一种抽象的规范。每个JVM 的实现都要遵守这样的规范&#xff0c;这样才能保证Java程序能够“一次编写&#xff0c;到处运行”。 内存模型描述了程序中各个变量&#xff08;实例域、静态域和数组元素&#xff09;之间的关系&#xff0c;以及在实际计算机系统中将…