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

news2024/10/1 12:25:22

目录

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

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

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

相关文章

企业知识管理系统安全是重中之重

企业开展知识管理工作的益处是全方位的&#xff0c;效果能从业务的各方面得到体现&#xff0c;最终效果就是企业竞争力的提升与企业经营业绩的提升。 知识管理系统的意义在于&#xff0c;构建系统的知识库&#xff0c;对纷杂的知识内容&#xff08;方案、策划、制度等&#xf…

【业务功能篇51】对象复制的三种方式 工具类Orika、反射、BeanUtils浅拷贝

业务场景&#xff1a; 设计规范前提下&#xff0c;我们数据层传输承载的是用DTO&#xff0c;而到控制层返回给前端会对应定义一个VO对象&#xff0c;比如是一个问题单数据集合list<DTO>,数据层接收的是DTO对对象&#xff0c;到控制层接收后需要转换成list<VO>,这里…

项目中如何使用文件IO?【大学学了好几门语言都有IO,到底怎么用?】

import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException;public class TestFile {public static void main(String[] args) throws IOException {// 通过这个简单的程序, 把一个文件的内容读取出…

Docker Compose 容器编排

Docker compose Docker compose 实现单机容器集群编排管理&#xff08;使用一个模板文件定义多个应用容器的启动参数和依赖关系&#xff0c;并使用docker compose来根据这个模板文件的配置来启动容器&#xff09; 通俗来说就是把之前的多条docker run启动容器命令 转换为docker…

关于云服务器ECS、宝塔的安装配置以及图床的使用

一、阿里云服务器的申请以及宝塔的安装 安装配置服务器的原理&#xff1a; step1&#xff1a;地址栏输入阿里云服务器官网地址 step2&#xff1a;在首页依次点击以下内容&#xff1a; step3&#xff1a;选择立即购买&#xff0c;并填写以下内容&#xff1a; step4&#xff1a…

6.运算符

6.1赋值运算符 ➢已经学过的赋值运算符&#xff1a; ➢其他赋值运算符&#xff1a; 、-、*、/、% 6.2 一元运算符 众多的JavaScript的运算符可以根据所需表达式的个数, 分为一元运算符、二元运算符、三元运算符 ●二元运算符: 例&#xff1a;let num1020 ●一元运算符: 例…

Vue3输入框(Input)

APIs 参数说明类型默认值必传width输入框宽度string | number‘100%’falseaddonBefore设置前置标签string | slot‘’falseaddonAfter设置后置标签string | slot‘’falseallowClear可以点击清除图标删除内容booleanfalsefalsepassword是否启用密码框booleanfalsefalsedisabl…

CSS3 Flexbox

Flex 是 Flexible Box 的缩写&#xff0c;意为弹性盒子布局。 CSS3中一种新的布局模式&#xff1a;W3C在2009年提出的一种布局方案&#xff0c;一种当页面需要适应不同的屏幕大小以及设备类型时确保元素拥有恰当的行为的布局方式。其目的是提供一种更加有效的方式来对一个容器…

回收站不见了?正确的2个操作方法!

大家有没有遇到回收站不见了的情况啊&#xff1f;真的很崩溃&#xff0c;我误删了一个比较重要的文件&#xff0c;想在回收站中把它还原&#xff0c;才发现我的回收站不见了&#xff01;这可咋整啊&#xff1f; 回收站是电脑操作系统中一个比较重要的功能&#xff0c;它可以帮助…

Ceph简介和特性

Ceph是一个多版本存储系统&#xff0c;它把每一个待管理的数据流(例如一个文件) 切分为一到多个固定大小的对象数据&#xff0c;并以其为原子单元完成数据存取。 对象数据的底层存储服务是由多个主机 (host) 组成的存储集群&#xff0c;该集群也被称之为 RADOS (ReliableAutoma…

DS18B20的原理及实例代码(51单片机、STM32单片机)

一、DS18B20介绍 DS18B20数字温度传感器是DALLAS公司生产的单总线器件&#xff0c;用它来组成一个测温系统具有线路简单&#xff0c;体积小&#xff0c;在一根通信线上可以挂很多这样的数字温度传感器&#xff0c;十分方便。 温度传感器种类众多&#xff0c;应用在高精度、高可…

快排、二路归并疑难杂症

蒟蒻小♥复习机试&#xff0c;记录一些疑点和注意点。 细节见代码注释 快排 快排中的边界条件判断需保证i<j&#xff0c;即满足label基准左边的数均小于右边的数。<的判断可能让子问题求解陷入死循环。 #include <iostream> #include <stdio.h> #include …

C++笔记之STL的sort使用第三个参数来自定义排序

C笔记之STL的sort使用第三个参数来自定义排序 code review! 文章目录 C笔记之STL的sort使用第三个参数来自定义排序1.方法一&#xff1a;使用比较函数(其实是使用函数指针)作为std::sort()的第三个参数来排序2.方法二&#xff1a;使用lambda表达式作为std::sort()的第三个参数…

IO进程线程day1(2023.7.25)

一、Xmind整理&#xff1a; 什么是IO&#xff1a; 文件IO函数与标准IO函数&#xff1a; 二、课上练习&#xff1a; 练习1&#xff1a;标准IO函数的简单示例 scanf: if(OS Linux) {去调用Linux的文件IOread(); } else if(OS windows) {去调用windows的文件IOfread(); …

垃圾回收回收阶段算法

当成功区分出垃圾的对象和存活的对象后&#xff0c;下面就是要开始回收了&#xff0c;目前在JVM中的垃圾回收阶段的算法有三种&#xff1a;标记——复制算法、标记——清除算法、标记——压缩算法。 1.标记——复制算法 将可用内存按照容量分为大小相等的两块&#xff0c;每次…

国标GB28181视频监控平台EasyGBS视频无法播放,抓包返回ICMP是什么原因?

国标GB28181视频平台EasyGBS是基于国标GB/T28181协议的行业内安防视频流媒体能力平台&#xff0c;可实现的视频功能包括&#xff1a;实时监控直播、录像、检索与回看、语音对讲、云存储、告警、平台级联等功能。国标GB28181视频监控平台部署简单、可拓展性强&#xff0c;支持将…

【软件测试】webdriver常用API演示(Java+IDEA+chrome浏览器)

1.元素定位方法 对象的定位应该是自动化测试的核心&#xff0c;要想操作一个对象&#xff0c;首先应该识别这个对象。一个对象就是一个人一样&#xff0c;他会有各种的特征&#xff08;属性&#xff09;&#xff0c;如比我们可以通过一个人的身份证号&#xff0c;姓名&#xf…

centos(linux)手动配置ip地址

查看系统 查看ip 进入网卡配置的目录 [root178-119-30-16 ~]# cd /etc/sysconfig/network-scripts/ [root178-119-30-16 network-scripts]# ls ifcfg-ens192 ifdown ifdown-ippp ifdown-post ifdown-sit ifdown-tunnel ifup-bnep ifup-ipv6 ifup-plus…

C#代码实现状态机

状态机四要素 现态、条件、动作、次态 现态&#xff1a;是指当前所处的状态 条件&#xff1a;又称为“事件”&#xff0c;当一个条件被满足&#xff0c;将会触发一个动作&#xff0c;或者执行一次状态转移。 动作&#xff1a;条件满足后执行的动作。动作执行完毕后&#xff0…

JavaScript |(一)JavaScript简介及基本语法 | 尚硅谷JavaScript基础实战

学习来源&#xff1a;尚硅谷JavaScript基础&实战丨JS入门到精通全套完整版 文章目录 &#x1f4da;JavaScript简介&#x1f407; 实现&#x1f407;JavaScript的特点 &#x1f4da;基本知识&#x1f407;编写位置&#x1f525;方式一&#xff1a;在标签中写&#xff08;不推…