2023.11.11 关于 Spring 中 Bean 的作用域

目录

Bean 的作用域

作用域的定义

Singleton(单例作用域)

Prototype(原型作用域)

Request(请求作用域)

Session(会话请求)

Application(全局作用域)

WebSocket(WebSocket 会话作用域 )

设置 Bean 的作用域

@Scope 注解

使用方式建议


Bean 的作用域

作用域的定义

  • 作用域(Scope)指变量、函数或对象在程序中可见和访问的范围
  • Bean 的作用域 指 Bean 在 Spring 整个框架中的某种 行为模式

Bean 的 6 种作用域

  • Spring 容器在初始化一个 Bean 的实例时,同时会指定该实例的作用域

Singleton(单例作用域)

  • 该作用域下的 Bean 在 Spring 容器中只存在一个实例:获取 Bean(即通过 context.getBean 等方法获取)及装配 Bean(通过 @Autowired 注入)都是同一个对象
  • Spring 容器在第一次请求时创建该实例,并在后续请求中返回相同的实例
  • 默认情况下,Spring 的 Bean 作用域为 Singleton
  • 因为 单例模式 的性能更好

Prototype(原型作用域)

  • 可理解为 多例作用域

  • 每次请求获取 Bean 时,都会创建一个新的实例

  • 每次获取 Bean 时都会返回一个新的对象,即原型作用域的 Bean 实例不会被共享

Request(请求作用域)

  • 在 Web 应用程序中,每个 HTTP 请求将创建一个新的 Bean 实例,并且该实例仅在当前请求的范围内可见
  • 对于每个请求,都会有一个单独的 Bean 实例

Session(会话请求)

  • 在 Web 应用程序中,每个用户会话均会创建一个新的 Bean 实例,并且该实例仅在当前用户会话的范围内可见
  • 对于每个用户会话,都会有一个单独的 Bean 实例

Application(全局作用域)

  • 在 Web 应用程序中,整个应用程序范围内只会创建一个Bean 实例,并且该实例将被共享和重用

WebSocket(WebSocket 会话作用域 )

  • 在基于 WebSocket 的应用程序中,每个 WebSocket 会话均会创建一个新的 Bean 实例,并且该实例仅在当前 WebSocket 会话的范围内可见

注意:

  • 在普通的 Spring 项目中只有前两种作用域,即前两种为 Spring 核心作用域
  • 后四种状态是 Spring MVC 中的作用域

建议阅读下文之前 点击下方链接了解 Lombok 的作用

Lombok 的作用和使用


实例理解

  • 我们先创建一个实体类 User
import lombok.Data;

@Data
public class User {
    public int id;
    public String name;
}
  • 再创建一个 UserBean 类,用来向 Spring 容器中 存储 User 类型的 Bean 对象
  • 此处向 Spring 容器中存入一个 id 为 user,且 name = 张三的 Bean 对象
import com.java.demo.enity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {
    @Bean
    public User user() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}
  • 新创建 UserController 类
  • 该类中新创建了一个 myUser 对象,且该对象赋值于注入进来的 user Bean 对象
  • 并修改 myUser 对象的 name 属性
  • 将从 user Bean 对象赋值而来的 name = 张三,修改为 name = 小林
import com.java.demo.enity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    @Autowired
    private User user;

    public void printUser() {
        System.out.println("UserController 类注入的 user Bean 对象,也就是 user 变量的初始值 -> " + user.toString());
//        修改 User
        User myUser = user;
        myUser.setName("小林");
        System.out.println("UserController 类中的 myUser 对象 -> " + myUser.toString());
        System.out.println("UserController 类修改 myUser 对象 name 属性后再次打印 user 变量 -> " + user.toString());
    }
}
  • 新创建 UserController2 类
  • 同样在该类中注入 user Bean 对象
import com.java.demo.enity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController2 {

    @Autowired
    private User user;

    public void printUser2() {
        System.out.println("UserController2 类注入的 user Bean 对象 -> " + user.toString());
    }
}
  • 最后再创建一个启动类
  • 该类用来从 Spring 容器中拿 UserController 类和 UserController2 类的 Bean 对象,并调用它们的成员方法
import com.java.demo.controller.UserController;
import com.java.demo.controller.UserController2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

        UserController userController = context.getBean("userController",UserController.class);
        userController.printUser();

        UserController2 userController2 = context.getBean("userController2",UserController2.class);
        userController2.printUser2();
    }
}

运行结果:

  • 该运行结果与我们的预期结果不同诶!
  • 为啥 UserController2 类中从 Spring 容器中拿到的 user Bean 对象其 name = 小林 ?

原因解释:

  • 正因为默认情况下,Spring 的 Bean 作用域为 Singleton,即 单例作用域
  • 即存储在 Spring 容器中的 id = user 的 Bean 对象仅有一份
  • 所以此处的 user 变量和 myUser 变量 均指向同一个对象(引用)
  • 即 myUser 变量在对其 name 进行修改时
  • 其他任何注入了 id = user 的 Bean 对象 的变量,也会跟着修改 name

设置 Bean 的作用域

@Scope 注解

  • 我们可以通过 @Scope 注解来设置 Bean 对象的作用域

实例理解

  • 如果我们不主动设置该 Bean 对象的作用域,其默认为 Singleton(单例作用域)
  • 所以为了解决上述实例问题所产生的问题,我们可以将该 Bean 对象主动设置为 prototype(原型作用域),即多例模式

方式一:直接在 @Scope 注解中填入 prototype

import com.java.demo.enity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {
    @Bean
    @Scope("prototype")
    public User user() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}

方式二:使用 ConfigurableBeanFactory 接口中定义的常量 SCOPE_PROTOTYPE,该常量表示原型作用域

import com.java.demo.enity.User;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public User user() {
        User user = new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}
  • 当我们修改完 Bean 对象的作用域时,再次运行启动类,观察运行结果

运行结果:

  •  UserController2 类中从 Spring 容器中拿到的 user Bean 对象其 name = 张三
  • 也就是说,在 UserController 类中,进行的修改操作并未影响到 Spring 容器中的初始 Bean 对象

使用方式建议

  • 上述两种方式起到的效果是相同的,但是推荐使用第二种方式
  • 使用第一种 直接填入 prototype 方式的前提是 你能够清楚的记得该单词是如何拼写的
  • 而第二种方式,IDEA 自带自动补全提示,可以保证我们不出错!

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

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

相关文章

第一百六十八回 NavigationBar组件

文章目录 1. 概念介绍2. 使用方法3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 我们在上一章回中介绍了"如何修改按钮的形状"相关的内容,本章回中将 介绍NavigationBar组件.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在本…

YOLOv8-Seg推理详解及部署实现

目录 前言一、YOLOv8-Seg推理(Python)1. YOLOv8-Seg预测2. YOLOv8-Seg预处理3. YOLOv8-Seg后处理4. YOLOv8-Seg推理 二、YOLOv8-Seg推理(C)1. ONNX导出2. YOLOv8-Seg预处理3. YOLOv8-Seg后处理4. YOLOv8推理 三、YOLOv8-Seg部署1. 源码下载2. 环境配置2.1 配置CMakeLists.txt2.…

【2023CANN训练营第二季】——Ascend C算子开发进阶—Ascend C Tiling计算

了解Tiling基本概念 在这一小节中接触到了一个新的概念,叫Tiling计算,指的是在Ascend C 算子开发过程中,矢量的算子流程分为3个基本任务:CopyIn,Compute,CopyOut。CopyIn任务负责将Global Memory上的输入T…

Vue3 源码解读系列(四)——组件更新

组件更新 组件更新流程: 从头部开始同步 从尾部开始同步 挂载剩余的新节点 删除多余的旧节点 处理未知的子序列 当两个节点类型相同时,执行更新操作当新子节点中没有旧子节点中的某些节点时,执行删除操作当新子节点中多了旧子节点中没有…

Android——Gradle插件gradle-wrapper.properties

一、Android Studio版本,Android Gradle插件版本,Gradle版本 Android Studio 通过Android Gradle插件 使用 Gradle来构建代码; Android Studio每次升级后, Android Gradle 插件自动更新,对应的Gradle版本也会变动&…

【MybatisPlus】条件构造器、自定义SQL、Service接口

🐌个人主页: 🐌 叶落闲庭 💨我的专栏:💨 c语言 数据结构 javaEE 操作系统 Redis 石可破也,而不可夺坚;丹可磨也,而不可夺赤。 MybatisPlus 一、条件构造器1.1 基于QueryW…

MemcachedRedis构建缓存服务器 (数据持久化,主从同步,哨兵模式)

Memcached/redis是高性能的分布式内存缓存服务器,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web等应用的速度、 提高可扩展性。降低数据库读的压力 Nsql的优点:高可扩展性,分布式计算,低成本,…

AI绘画神器DALLE 3的解码器:一步生成的扩散模型之Consistency Models

前言 关于为何写此文,说来同样话长啊,历程如下 我司LLM项目团队于23年11月份在给一些B端客户做文生图的应用时,对比了各种同类工具,发现DALLE 3确实强,加之也要在论文100课上讲DALLE三代的三篇论文,故此文…

javax.management.InstanceNotFoundException: Catalina:type=Server错误的解决

软件: JDK 1.8 Tomcat 8.5.66 IDEA 2019.3.3 问题:启动IDEA新建一Web Application项目,设置好项目运行,结果发现提示: 提示:Application Server was not connected before run configuration stop, rea…

【AI】生成模型变得简单:了解它们的工作原理和不同类型

什么是生成模型? 在不断发展的人工智能领域,生成模型已成为人工智能技术最具吸引力和创造力的方面之一。这些模型是创意人工智能的核心,它们有能力生成各种内容,从栩栩如生的图像和引人入胜的文本到令人着迷的音乐和创新的艺术作…

linux入门---线程池的模拟实现

目录标题 什么是线程池线程的封装准备工作构造函数和析构函数start函数join函数threadname函数完整代码 线程池的实现准备工作构造函数和析构函数push函数pop函数run函数完整的代码 测试代码 什么是线程池 在实现线程池之前我们先了解一下什么是线程池,所谓的池大家…

【postgresql】CentOS7 安装pgAdmin 4

CentOS7 安装PostgreSQL Web管理工具pgAdmin 4。 pgAdmin 是世界上最先进的开源数据库 PostgreSQL 最受欢迎且功能丰富的开源管理和开发平台。 下载地址: pgadmin-4 download pgAdmin 4分为桌面版和服务器版。 我们这里部署服务器版本。 安装RPM包。 安装源 s…

【数据结构】树与二叉树(十一):二叉树的层次遍历(算法LevelOrder)

文章目录 5.2.1 二叉树二叉树性质引理5.1:二叉树中层数为i的结点至多有 2 i 2^i 2i个,其中 i ≥ 0 i \geq 0 i≥0。引理5.2:高度为k的二叉树中至多有 2 k 1 − 1 2^{k1}-1 2k1−1个结点,其中 k ≥ 0 k \geq 0 k≥0。引理5.3&…

最长有效括号

给你一个只包含 ‘(’ 和 ‘)’ 的字符串&#xff0c;找出最长有效&#xff08;格式正确且连续&#xff09;括号子串的长度。 class Solution {public int longestValidParentheses(String s) {Stack<Integer> st new Stack<Integer>();int ans 0;for(int i 0…

Linux C 时间编程

时间编程 Linux中时间相关命令时间编程time  获取当前的时间gmtime  获取当前日期时间localtime  获取本地时间日期asctime  规格时间结构体为字符串 Linux中时间相关命令 1&#xff09;date&#xff1a;打印当前的系统时间。 2&#xff09;date -s 20231111&#xff…

封神教程:腾讯云3年轻量应用服务器老用户购买方法

腾讯云轻量应用服务器特价是有新用户限制的&#xff0c;所以阿腾云建议大家选择3年期轻量应用服务器&#xff0c;一劳永逸&#xff0c;免去续费困扰。腾讯云轻量应用服务器3年优惠可以选择2核2G4M和2核4G5M带宽&#xff0c;3年轻量2核2G4M服务器540元&#xff0c;2核4G5M轻量应…

Linux 进程优先级 | 环境变量

目录 进程优先级 基本概念 认识优先级 PRI and NI NI值的范围 查看进程优先级 用top命令更改已存在进程的nice&#xff1a; 如何修改优先级 其他概念 环境变量 基本概念 常见环境变量 和环境变量相关的命令 环境变量的组织方式 通过代码如何获取环境变量 环境变量通…

arcgis基础篇--实验

一、绘制带空洞的面要素 方法一&#xff1a;先绘制出一个面区域&#xff0c;然后在面上再绘制一个面区域代表面洞&#xff0c;两者位于同一个图层内&#xff0c;选中代表面洞的区域&#xff0c;选择【编辑器】-【裁剪】工具&#xff0c;将面裁剪出一个洞&#xff0c;随后删除代…

SpringBoot--中间件技术-2:整合redis,redis实战小案例,springboot cache,cache简化redis的实现,含代码

SpringBoot整合Redis 实现步骤 导pom文件坐标 <!--redis依赖--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>yaml主配置文件&#xff0c;配置…