Spring框架学习 -- Bean的生命周期和作用域

目录

前言

案例

案例分析

作用域的定义 

Bean对象的6种作用域 

Singleton

prototype

设置作用域 

​编辑延迟初始化

Spring的执行流程 

Bean的生命周期


前言

        我们可以类比一下普通变量的生命周期和作用域, 大多数变量的生命周期和作用域都被限定在了花括号内 {}, 除了这个大括号, 这个变量也就会被销毁, 与之对应的内存也会被JVM回收.

        对于类中的变量, 即使是出了{}, 也仍然能被访问到.

        前面的内容是如何存储和获取Bean对象, 因此spring中最核心的资源就是Bean对象, 那么Bean的作用域是怎么样的呢?

        下面我们通过一个案例来演示一下:

案例

        假设现在有一个公共的Bean对象提供给用户A和用户B使用, 然后在使用的图中,用户A 却悄咪咪的修改了公共的Bean资源, 导致B在使用的时候发生了预期之外的逻辑错误.

        下面是代码实现:

        首先我们实现Bean对象所属的类:

public class User {private int age;private String name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

        创建Bean对象:

@Component
public class Users {@Beanpublic User getUser() {User user = new User();user.setAge(18);user.setName("如花");return user;}
}

        属性注入:

// controller1 相当于A@Controller
public class Controller1 {@Autowiredpublic User user;public void useUser() {User user1 = user;System.out.println("公共类原来的名字为:" + user1.getName());System.out.println("A 使用之前将其修改为 翠花");user1.setName("翠花");System.out.println("现在公共类的名字为:" + user1.getName());}
}
@Controller
public class Controller2 {@Autowiredpublic User user;public void userUser() {User user1 = user;System.out.println("B 使用这个公共Bean类的名字为:" + user1.getName());System.out.println("但是B想念的是如花");}}

        添加启动类:

public class Main {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("springConfig.xml");Controller1 controllerA = context.getBean("controller1", Controller1.class);controllerA.useUser();Controller2 controllerB = context.getBean("controller2", Controller2.class);controllerB.userUser();}
}

-- 输出:

案例分析

        上面问题的原因就是Bean默认情况下是单例状态的(singleton), 也就是所有人使用的都是同一个对象, 之前我们学过单例模式的时候就知道, 使用单例模式可以极大的提高性能, 但是同时也带来了新的安全问题.

        spring中Bean的作用域默认也是singleton的单例模式.

作用域的定义 

        限定程序中变量的可用范围, 我们把这种行为叫做修改作用域, 也就是说这个可用范围对应于这个作用域, 或者说是在源代码中定义变量的某个区域就叫做作用域.

        而Bean的作用域是指在spring整个framework框架中的某个行为, 所以Bean通过不同的行为类型, 来决定定义域, 比如singleton单例作用域, 就表示这个Bean对象是在整个spring中只有一份, 他是全局共享的, 那么其他人修改这个值之后, 另外一个人读取到这个值他就是被修改的值.

        由Spring容器创建的Bean的生存周期被称为Bean的作用域, 默认情况下, 由spring容器创建的所有Bean对象都是Singleton作用域, 换句话说, 针对Bean定义只创建了一个Bean对象, 并有spring容器可以在整个应用程序生存期中使用该实例, 当不同的Bean与不同的层(控制层, 服务层 和 数据访问对象层) 相对应时, 可以使用Singleton作用域.

        spring支持的第二种作用域就是prototype, 类似于java代码中使用new操作来创建Bean对象, 每次需要在容器中或者通过Bean引用其他Bean定义中, 又或者通过显示Bean查询(getBean) 从应用程序中访问Prototype作用域时就会创建他们:

        注意这里的scope = "prototype"

 

Bean对象的6种作用域 

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

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

        注意后面四种是spring MVC中的值, 在普通的spring项目中, 只有目前两种

        下面介绍Spring中常用的两种作用域 >>

Singleton

  • 描述: 该作用域下的Bean在IOC容器中只存在一个实例, 通过ApplicationContext,getBean方法获取的Bean和通过@Autowired或者@Resource等注入的Bean都是同一个对象
  • 场景: 通过无状态的Bean使用该作用域, 无状态表示Bean对象的属性状态不需要更新
  • 备注: Spring默认选择该作用域

prototype

  •  描述: 每次该作用域下的Bean请求都会创建新的实例: 不管是通过spring上下文(ApplicationContext) 或者是通过属性注入Bean, 都是新的对象的实例
  • 场景: 通常有状态的Bean使用该作用域

设置作用域 

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

@Component
public class Users {@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public User getUser() {User user = new User();user.setAge(18);user.setName("如花");return user;}
}

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

  1. 直接设置值: @Scope("prototype")
  2. 使用枚举设置: @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

        或者是在xml配置文件中的Bean对象中添加scope属性:

延迟初始化

        默认情况下, spring容器在启动阶段创建Bean, 该过程被称为预先Bean初始化. 它的有点就是可以尽早发现配置错误. 例如在基于XML文件的配置中, Bean的定义的类属性出现了错别字, 或者引用了一个不可用的Bean定义等.

        但是从另外一方面, 如果存在大量的Bean定义, 或者一些特殊的Bean, 例如Hibernate SessionFactory 或者JPA EntityManagerFactory(这些Bena 的初始化会占用大量时间), 那么初始化可能会降低启动的速度, 一些Bean可能只在特定的场合中使用, 在其他情况下并不被需要, 在这种情况下的话, 预先初始化会导致不必要的内存消耗和性能消耗.

        spring还支持延迟Bean初始化, 如果开发人员将Bean的配置为延迟创建, 那么容器将会延迟Bean的创建, 直到被需要的时候才被创建, 通过从一个已经创建的另外一个Bean中的引用, 或者是显式的调用getBean来查找Bean, 都可以触发Bean的创建.

@Controller
public class Controller1 {@Autowiredpublic User user;public void useUser() {User user1 = user;System.out.println("公共类原来的名字为:" + user1.getName());System.out.println("A 使用之前将其修改为 翠花");user1.setName("翠花");System.out.println("现在公共类的名字为:" + user1.getName());}
}

        例如上述代码, 如果这里的Controller1这个类中的属性user使用的是延迟初始化, 那么只有在使用的时候才会被创建, 例如执行到User1 = user的时候就会被创建和初始化.  

        在基于XML的配置中, 可以在<bean> 标签中使用lazy-init 的字段将Bean设置为延迟初始化.

        如果想在一个XML文件中将所有的Bean对象都定义为延迟初始化, 可以使用<beans>标签的default-lazy-init的字段.

         延迟Bean的创建的优点是加快了容器的启动时间, 并且占用了较少的内存, 但是另外一方面, 如果在元数据中存在Bean的配置错误, 在对方案进行测试之前, 将无法发现这些错误.

        将Bean定义为延迟初始化的时候需要格外注意, 如果其中一个依赖Bean被定义为预先初始化, 那么延迟定义将会没有任何作用, 预先Bean定义在启动期间被处理, 所以它也会触发处理延迟Bean的定义.

Spring的执行流程 

 

 spring的执行流程分为下面几点:

  1. 启动容器, 加载XML配置文件
  2. 根据配置完成Bean初始化:
    1. 添加扫描路径
    2. 识别扫描路径下的类注解, 识别Bean对象
  3. 注册Bean对象到spring容器中

    如果此Bean对象依赖于其他Bean对象的, 可以使用@Autowired和@Resource注解
  4. 装配Bean属性

总结下来就是: 

->启动 Spring 容器

-> 实例化 Bean(分配内存空间,从⽆到有)

-> Bean 注册到 Spring 中(存操作)

-> 将 Bean 装配到需要的类中(取操作, getBean等)

Bean的生命周期

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

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

  1. 实例化Bean, 为Bean分配内存空间
  2. 设置属性(Bean注入)
  3. Bean初始化
    1. 实现了各种Aware通知的方法, 如BeanNameAware, BeanFactoryAware,ApplicationContextAware
    2. 执行BeanPostProcessor初始化方法
    3. 执行@PostConstruct 初始化方法, 依赖注入后被执行
    4. 指定自己指定的init方法
    5. 执行BeanPostProcessor初始化后置方法
  4. 使用Bean
  5. 销毁Bean
  6. 销毁容器的各种⽅法,如 @PreDestroy、DisposableBean 接⼝⽅法、destroy-method
     

流程图如下:

 

 

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

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

相关文章

mysql索引分为哪几类,聚簇索引和非聚簇索引的区别,MySQL索引失效的情况有哪几种情况,MySQL索引优化的手段,MySQL回表

文章目录 索引分为哪几类&#xff1f;聚簇索引和非聚簇索引的区别什么是[聚簇索引](https://so.csdn.net/so/search?q聚簇索引&spm1001.2101.3001.7020)&#xff1f;&#xff08;重点&#xff09;非聚簇索引 聚簇索引和非聚簇索引的区别主要有以下几个&#xff1a;什么叫回…

python-选择排序

选择排序是一种简单直观的排序算法&#xff0c;它的基本思想是每一轮选择未排序部分的最小元素&#xff0c;然后将其放到已排序部分的末尾。这个过程持续进行&#xff0c;直到整个数组排序完成。(重点&#xff1a;通过位置找元素) 以下是选择排序的详细步骤和 Python 实现&…

原生小程序图表

原生小程序使用图表 话不多说直接进入正题 官方文档: https://www.ucharts.cn/v2/#/ 下载文件 首先去gitee上把文件下载到自己的项目中 https://gitee.com/uCharts/uCharts 找到微信小程序和里面的组件 把里面src下的文件全部下载下来放入自己项目中 项目文件 新建文件…

为何百兆静态库能打进数兆的可执行文件?

第三方库是工程开发必不可少的部分&#xff0c;而第三方库可以是.a和.framework的静态库&#xff0c;也可以是.framework的动态库&#xff0c;其中静态库是最常用的方式。 静态库往往比较大&#xff0c;可在打包到可执行文件之后&#xff0c;对安装包大小的增加远远小于静态库本…

党建信息管理系统源码 支持在线交党费 附带完整的搭建教程

传统的党建管理模式通常采用手工方式&#xff0c;不仅效率低下&#xff0c;而且容易出错。随着组织规模的扩大和党员数量的增加&#xff0c;这种管理方式已经无法满足现实需求。此外&#xff0c;传统的党建管理模式缺乏在线交党费功能&#xff0c;给党员带来不便。因此&#xf…

Linux 常见命令篇

history 获取执行的指令记录 语法格式: history [参数] 常用参数&#xff1a; -a 写入命令记录 -c 清空命令记录 -d 删除指定序号的命令记录 -n 读取命令记录 -r 读取命令记录到缓冲区 -s 将指定的命令添加到缓冲区 -w 将缓冲区信息写入到历史文件 history#获取最近的三条…

使用git下载远程所有分支到本地

使用git下载远程所有分支到本地&#xff1a; 打开gitbash 输入以下命令即可&#xff1a; git clone git地址 cd git文件夹 git branch -r | grep -v \-> | while read remote; do git branch --track "${remote#origin/}" "$remote"; done git fetch -…

如何用低代码的思路设计文字描边渐变组件

前言 文字特效设计一直是困扰 Web 前端 Css 世界多年的问题, 比如如何用纯 Css 实现文字描边, 渐变, 阴影等, 由于受限于浏览器兼容性的问题, 我们不得不使用其他替代方案来实现. 平时工作中我们使用 PS 等设计工具能很容易的实现文字渐变等特效, 但是随着可视化技术的成熟, 我…

IBM X3650M4安装ESXI6.5卡在/lsl_mr3.v00

环境&#xff1a;IBM X3650M4服务器双盘配置raid1&#xff0c;通过rufus制作启动U盘&#xff0c;安装VMware Vsphere 5.5系统 问题&#xff1a;卡在/lsi_mr3.v00界面无法往下运行&#xff08;两台配置一样的机器遇到同样的问题&#xff09; 解决方案&#xff1a; 直接在U盘根…

Spring 七大组件

文章目录 Spring 七大组件 Spring 七大组件 核心容器(Spring core) 核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean&#xff0c;它是工厂模式的实现。BeanFactory使用控制反转(IOC)模式…

Diffusion Model: DDPM

本文相关内容只记录看论文过程中一些难点问题&#xff0c;内容间逻辑性不强&#xff0c;甚至有点混乱&#xff0c;因此只作为本人“备忘”&#xff0c;不建议其他人阅读。 Denoising Diffusion Probabilistic Models: https://arxiv.org/abs/2006.11239 DDPM 一、基于 已知…

【开源】基于Vue和SpringBoot的农家乐订餐系统

项目编号&#xff1a; S 043 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S043&#xff0c;文末获取源码。} 项目编号&#xff1a;S043&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户2.2 管理员 三、系统展示四、核…