5. Bean 的作用域和生命周期

目录

1. Bean 被修改的案例 

2. 作用域定义

2.1 Bean 的 6 种作用域

singleton

prototype

request

session

application(了解)

websocket (了解)

 单例作用域(singleton)VS 全局作用域(application)

2.2 设置作用域

3. Spring 的执行流程和 Bean 的生命周期

3.1 Spring 执行流程

3.2 Bean 的生命周期

3.3 实例化和初始化的区别


在之前的文章中,我们学习到 Spring 是用来读取和存储 Bean 的,因此,在 Spring 中 Bean 是最核心的操作资源,所以我们接下来学习 Bean 的作用域和生命周期。

1. Bean 被修改的案例 

@Controller
public class ScopeController {
    @Autowired
    private Users user;

    public void sayHi(){
        System.out.println(user);
        user.setName("张三获取的user");
        System.out.println(user);
    }
}
public class App {
    public static void main(String[] args) {
        // 得到 Spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        ScopeController scopeController = context.getBean(ScopeController.class);
        scopeController.sayHi();
    }
}

运行后可以看到 name 已经被修改了。

然后我们再次新建一个类,同样去使用这个对象:

@Controller
public class ScopeController2 {
    @Autowired
    private Users user;

    public void sayHi(){
        System.out.println(user);
    }
}
public class App {
    public static void main(String[] args) {
        // 得到 Spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        ScopeController scopeController = context.getBean(ScopeController.class);
        scopeController.sayHi();

        ScopeController2 scopeController2 = context.getBean(ScopeController2.class);
        scopeController2.sayHi();
    }
}

根据运行结果可以看到,当我们再次去获取 Bean 时,拿到的就是修改后的 Bean。那么,这种情况就是单例模式(在一个应用中,一个对象只有一份,无论经过多少线程进行修改,拿到的都是同一份)。Bean 默认情况下是单例状态(singleton),也就是所有人使用的是同一个对象,使用单例模式可以很大程度上提高性能,所以在 Spring 中 Bean 的作用域默认也是 singleton 单例模式。

那么当我们在启动类中打开两个 Application (多例)时,结果是不同的:

public class App2 {
    public static void main(String[] args) {
        // 得到 Spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        ScopeController scopeController = context.getBean(ScopeController.class);
        System.out.println(scopeController);

        ApplicationContext context2 = new ClassPathXmlApplicationContext("spring-config.xml");
        ScopeController2 scopeController2 = context2.getBean(ScopeController2.class);
        System.out.println(scopeController2);
    }
}

2. 作用域定义

限定程序中变量的可用范围叫做作用域,或者说在源代码中定义变量的某个区域就叫做作用域。 而 Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式,比如 singleton 单例作用域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他人修改了这个值之后,那么另⼀ 个人读取到的就是被修改的值。

2.1 Bean 的 6 种作用域

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作用域。从官方文档中,我们可以看到 bean 的作用域有 6 种:

Spring有 6 种作用域,最后四种是基于 Spring MVC 生效的:

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

后四种状态是 Spring MVC 中的至,在普通的 Spring 项目中只有前两种。 

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默认选择该作用域
prototype
  •  官方说明:Scopes a single bean definition to any number of object instances.
  • 描述:每次对该作用域下的 Bean 的请求都会创建新的实例:获取 Bean(即通过 applicationContext.getBean 等方法获取)及装配Bean(即通过@Autowired注入)都是新的对象实例。
  • 场景:通常有状态的Bean使⽤该作⽤域
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 中使用
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中使用
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 中使用
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 中使用
 单例作用域(singleton)VS 全局作用域(application)
  1. singleton 是 Spring Core 的作用域;application 是 Spring Web 中的作用域。
  2. singleton 作用于 IoC 容器;application 作用于 Servlet 容器;

Application scope 就是对于整个 web 容器来说,bean 的作用域是 ServletContext 级别的,这个和 singleton 有点类似,但是区别在于,Applicationscope 是 ServletContext 的单例,singleton 是一个 ApplicationContext 的单例。

一个 web 服务只有一个 ServletContext,可以有多个 ApplicationContext。

2.2 设置作用域

还可以直接设置 Bean 的作用域,此处我们使用 @Scope 标签来声明 bean 的作用域为:prototype:

@Configuration
public class BeanConfig {
    @Bean
    public Integer age(){
        return 15;
    }
  //  @Bean({"aaa","user"})
    @Bean
    public Users user(Integer age){
        Users user = new Users();
        user.setName("小明");
        user.setAge(age);
        return user;
    }
    @Scope("prototype")
    @Bean
    public Users user2(){
        Users user = new Users();
        user.setName("小蓝");
        user.setAge(19);
        return user;
    }
}
@Controller
public class ScopeController2 {
    @Autowired
    private Users user2;

    public void sayHi(){
        System.out.println(user2);
    }
}
public class App {
    public static void main(String[] args) {
        // 得到 Spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        ScopeController scopeController = context.getBean(ScopeController.class);
        scopeController.sayHi();

        ScopeController2 scopeController2 = context.getBean(ScopeController2.class);
        scopeController2.sayHi();
    }
}

可以看到修改后再次获取时,获取到的仍然是原先的 Bean。

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

1. 直接设置值:

@Scope("prototype")

2. 使用枚举设置: 

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

3. Spring 的执行流程和 Bean 的生命周期

3.1 Spring 执行流程

  1. 启动容器
  2. 解析配置文件的内容,并创建 Bean
  3. 把对象放在容器中
  4. Bean 依赖对象的装配

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

3.2 Bean 的生命周期

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

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

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

2. 设置属性(Bean 注入和装配,比如 @Autowired)

3. Bean 初始化

  • 执行各种通知,如 BeanNameAware、BeanFactoryAware、 ApplicationContextAware 的接口方法;
  • 执行初始化前置方法;
    • xml 定义 init-method
    • 使用注解的方式  @PostConstruct
  • 执行 @PostConstruct 初始化方法,依赖注入操作之后被执行;
  • 执行 BeanPostProcessor 初始化后置方法。

4. 使用 Bean

5. 销毁 Bean

  • 销毁容器的各种方法,如 @PreDestroy、DisposableBean 接口方法、destroy-method。

接下来,我们来看一下前置方法 BeanNameAware:

@Component
public class BeanLifeComponent implements BeanNameAware {

    @Override
    public void setBeanName(String s) {
        System.out.println("设置beanname" + s);
    }
    public void hi(){
        System.out.println("hi~");
    }
}
public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        BeanLifeComponent beanLifeComponent = context.getBean(BeanLifeComponent.class);
        beanLifeComponent.hi();
    }
}

初始化方法 PostConstruct :

@Component
public class BeanLifeComponent implements BeanNameAware {

    @Override
    public void setBeanName(String s) {
        System.out.println("设置beanname" + s);
    }
    
    @PostConstruct
    public void postConstruct(){
        System.out.println("执行postConstruct方法");
    }
    public void hi(){
        System.out.println("hi~");
    }
}
public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        BeanLifeComponent beanLifeComponent = context.getBean(BeanLifeComponent.class);
        beanLifeComponent.hi();
    }
}

当我们没有调用 hi 方法时,依然会执行以上 PostConstruct 方法。

执行 init 方法:

<?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="SpringCoreDemo.component"></content:component-scan>
    <bean id="beanlife" class="SpringCoreDemo.component.BeanLifeComponent" init-method="init"></bean>

</beans>
@Component
public class BeanLifeComponent implements BeanNameAware {

    @Override
    public void setBeanName(String s) {
        System.out.println("设置beanname:" + s);
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("执行postConstruct方法");
    }

    public void init(){
        System.out.println("执行 init 方法");
    }
    public void hi(){
        System.out.println("hi~");
    }
}

因此,我们可以知道: postConstruct 在对象初始化时,都会执行;init 只对 xml 中定义的 bean 生效;先执行注解,再执行 xml 中配置的 init-method;

我们再来看使用 @Predestroy 完成对象的销毁:

@Component
public class BeanLifeComponent implements BeanNameAware {

    @Override
    public void setBeanName(String s) {
        System.out.println("设置beanname:" + s);
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("执行postConstruct方法");
    }

    public void init(){
        System.out.println("执行 init 方法");
    }
    @PreDestroy
    public void destroy(){
        System.out.println("执行destroy方法...");
    }
    public void hi(){
        System.out.println("hi~");
    }
}

直接运行以上代码可以看到并没有执行 destroy 方法,因此我们需要先将容器销毁:

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        context.destroy();
    }
}

 此时可以看到执行了 destroy 方法,同样可以通过 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="SpringCoreDemo.component"></content:component-scan>-->
    <bean id="beanlife" class="SpringCoreDemo.component.BeanLifeComponent" init-method="init" destroy-method="destroyXML"></bean>

</beans>
@Component
public class BeanLifeComponent implements BeanNameAware {

    @Override
    public void setBeanName(String s) {
        System.out.println("设置beanname:" + s);
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("执行postConstruct方法");
    }

    public void init(){
        System.out.println("执行 init 方法");
    }
    @PreDestroy
    public void destroy(){
        System.out.println("执行destroy方法...");
    }
    public void destroyXML() {
        System.out.println("执行destroyXML方法...");
    }
        public void hi(){
        System.out.println("hi~");
    }
}
public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        context.destroy();
    }
}

可以看到同样执行成功。 

3.3 实例化和初始化的区别

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JVM堆内存介绍

一&#xff1a;JVM中内存 JVM中内存通常划分为两个部分&#xff0c;分别为堆内存与栈内存&#xff0c;栈内存主要用运行线程方法 存放本地暂时变量与线程中方法运行时候须要的引用对象地址。 JVM全部的对象信息都 存放在堆内存中。相比栈内存&#xff0c;堆内存能够所大的多&am…

web-vim信息泄露

&#xff08;1&#xff09;知识补充 vim 交换文件名 在使用vim时会创建临时缓存文件&#xff0c;关闭vim时缓存文件则会被删除&#xff0c;当vim异常退出后&#xff0c;因为未处理缓存文件&#xff0c;导致可以通过缓存文件恢复原始文件内容   以 index.php 为例&#xff1…

Windows 10, version 22H2 (updated Jul 2023) 中文版、英文版下载

Windows 10, version 22H2 (updated Jul 2023) 中文版、英文版下载 Windows 10 22H2 企业版 arm64 x64 请访问原文链接&#xff1a;https://sysin.org/blog/windows-10/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Window…

基于C++的QT基础教程学习笔记

文章目录&#xff1a; 来源 教程社区 一&#xff1a;QT下载安装 二&#xff1a;注意事项 1.在哪里写程序 2.如何看手册 3.技巧 三&#xff1a;常用函数 1.窗口 2.相关 3.按钮 4.信号与槽函数 5.常用栏 菜单栏 工具栏 状态栏 6.铆接部件 7.文本编辑 8…

postman接口测试实战讲解

目录 背景描述 创建一个GET请求 在pre-request scripts构建签名 脚本写在环境变量中 postman console的用法 Collection Runner 自动化API测试 创建接口的测试用例 选择并运行自动化接口测试 测试结果 有还不懂的同学可以找我拿演示视频喔 背景描述 有一个项目要使…

10分钟内入门 ArcGIS Pro

本文来源&#xff1a;GIS荟 大家好&#xff0c;这篇文章大概会花费你10分钟的时间&#xff0c;带你入门 ArcGIS Pro 的使用&#xff0c;不过前提是你有 ArcMap 使用经验。 我将从工程文件组织方式、软件界面、常用功能、编辑器、制图这5个维度给大家介绍。 演示使用的 ArcGI…

element-ui 表格没有内容点击插入数据,有内容点击删除(vue)

记录一下&#xff0c;希望能够帮到大家。 <template><div><div class"tabs" style"display: flex;line-height: 20px"><button href"javascript:;" :class"{active: dialogFormVisible3}" click"dialogForm…

[uni-app] 微信小程序 - 组件找不到/导入报错 (分包问题导致)

文章目录 问题表现问题原因 问题表现 切换了个路径下的组件, 导入失败, 尝试了清缓存\重启\删项目等一些列操作均无效 上面两个路径中, 都存在一模一样的videItem.vue Main路径是可以导入的 Main路径是无法导入的 问题原因 后来发现, 是 分包的问题导致. 我们先来假设一个场…

MySQL 主从复制的认识 2023.07.23

一、理解MySQL主从复制原理 1、概念&#xff1a;主从复制是用来建立一个和 主数据库完全一样的数据库环境称为从数据库&#xff1b;主数据库一般是准实时的业务数据库。 2、作用&#xff1a;灾备、数据分布、负载平衡、读写分离、提高并发能力 3、原理图 4、具体步骤 (1) M…

mysql(二)SQL语句

目录 一、SQL语句类型 二、数据库操作 三、数据类型 四、创建 五、查看 六、更改 七、增、删、改、查 八、查询数据 一、SQL语句类型 SQL语句类型&#xff1a; DDL DDL&#xff08;Data Definition Language&#xff0c;数据定义语言&#xff09;&#xff1a;用于…

基于深度神经网络的肺炎检测系统实现

一、说在前面 使用AI进行新冠肺炎图像诊断可以加快病例的诊断速度&#xff0c;提高诊断的准确性&#xff0c;并在大规模筛查中发挥重要作用&#xff0c;从而更好地控制和管理这一流行病。然而&#xff0c;需要强调的是&#xff0c;AI技术仅作为辅助手段&#xff0c;最终的诊断决…

kafka消费者api和分区分配和offset消费

kafka消费者 消费者的消费方式为主动从broker拉取消息&#xff0c;由于消费者的消费速度不同&#xff0c;由broker决定消息发送速度难以适应所有消费者的能力 拉取数据的问题在于&#xff0c;消费者可能会获得空数据 消费者组工作流程 Consumer Group&#xff08;CG&#x…

夯实数字化转型安全地基,华东某农商行开源安全治理经验

华东某农村商业银行是一家全国首批组建的股份制农村金融机构。近年来&#xff0c;该农商行坚持“科技强行”战略&#xff0c;进一步夯实数字化核心基础&#xff0c;积极推动金融科技与产品、服务的深度融合&#xff0c;努力拓展数字金融的包容性&#xff0c;让数字金融更有温度…

《重构的时机和方法》一本值得程序员都认真读的书

写在前面 《重构的时机和方法》是一本关于软件开发中重构技术的书籍。它以独特的风格和内容优势&#xff0c;为读者提供了全面而易于理解的指导&#xff0c;帮助他们在实际项目中应用重构技术&#xff0c;提高代码质量和开发效率。这本书由两个不同风格的部分组成&#xff0c;…

Hadoop生态体系-HDFS

目录标题 1、Apache Hadoop2、HDFS2.1 设计目标&#xff1a;2.2 特性&#xff1a;2.3 架构2.4 注意点2.5 HDFS基本操作2.5.1 shell命令选项2.5.2 shell常用命令介绍 3、HDFS基本原理3.1 NameNode 概述3.2 Datanode概述 1、Apache Hadoop Hadoop&#xff1a;允许使用简单的编程…

RocketMQ重复消费的解决方案::分布式锁直击面试!

文章目录 场景分析方法的幂等分布式锁Redis实现分布式锁抢锁的设计思路 分布式锁案例 直击面试rocketmq什么时候重复消费消息丢失的问题消息在哪里丢失发送端确保发送成功并且配合失败的业务处理消费端确保消息不丢失rocketmq 主从同步刷盘 场景分析 分布式系统架构中,队列是分…

7.python设计模式【桥结模式】

内容&#xff1a;将一个事物的两个维度分离&#xff0c;使其都可以独立变化角色&#xff1a; 抽象&#xff08;Abstraction&#xff09;细化抽象&#xff08;RefinedAbstraction&#xff09;实现者&#xff08;Implementor&#xff09;具体实现者&#xff08;ConcreteImplement…

vue3 +ts 报错 index.vue 不是模块

那是因为index.vue中创建了一个空的script标签&#xff0c;而且语法使用的是ts语法。vue-cli会用ts语法解析和校验 如果是无状态组件&#xff0c;删掉 如果是有状态组件&#xff0c;导出该组件的实例 去掉null的script后&#xff1a;

实验三 贪心算法

实验三 贪心算法 迪杰斯特拉的贪心算法实现 优先队列等 1.实验目的 1、掌握贪心算法的基本要素 &#xff1a;最优子结构性质和贪心选择性质 2、应用优先队列求单源顶点的最短路径Dijkstra算法&#xff0c;掌握贪心算法。 2.实验环境 Java 3.问题描述 给定带权有向图G (V…

单Bank OTA升级:STM32G071 APP (二)

接上一篇文章&#xff1a;单Bank OTA升级&#xff1a;STM32G071 BootLoader (一)&#xff1a;跳转链接 什么是单Bank升级&#xff1a;将Flash划分为以下3个区域。 BootLoader区&#xff1a;程序进行升级的引导程序&#xff0c;根据Upade_Flag来判断跳转Bank区运行程序或是接收…