探秘Spring Bean的秘境:作用域篇【beans 三】

news2024/11/26 16:49:09

欢迎来到我的博客,代码的世界里,每一行都是一个故事


在这里插入图片描述

探秘Spring Bean的秘境:作用域篇【beans 三】

    • 前言
    • 单例作用域
      • 如何声明单例Bean:
      • 特点:
    • 原型作用域
      • 如何声明原型Bean:
      • 特点:
    • 会话作用域
      • 如何声明会话作用域Bean:
      • 特点:
    • 请求作用域
      • 如何声明请求作用域Bean:
      • 特点:
    • 自定义作用域
    • 作用域的生命周期
      • 单例作用域:
      • 原型作用域:
      • 影响:
    • 实际应用场景
      • 1. 单例作用域(Singleton Scope):
      • 2. 原型作用域(Prototype Scope):
      • 3. 会话作用域(Session Scope):
      • 4. 请求作用域(Request Scope):

前言

在软件开发的舞台上,每个角色都有其独特的作用。Spring框架中的Bean也一样,通过作用域的不同,它们可以在应用中扮演不同的角色。从独一无二的单例到即时生成的原型,Spring的作用域机制为我们提供了丰富的选择。在这篇文章中,我们将带你踏上一场Spring Bean的时空之旅,深入了解每个作用域的特性和适用场景。

单例作用域

在Spring中,单例作用域(Singleton Scope)是默认的Bean作用域之一。当一个Bean被定义为单例时,Spring容器会在整个应用中维护该Bean的单一实例。每次请求该Bean时,都会返回相同的实例。这有助于节省资源,尤其是对于那些昂贵或频繁使用的对象。

如何声明单例Bean:

  1. 使用XML配置:
<bean id="mySingletonBean" class="com.example.MySingletonBean" scope="singleton">
    <!-- 其他配置属性 -->
</bean>
  1. 使用Java配置:
@Configuration
public class AppConfig {

    @Bean
    public MySingletonBean mySingletonBean() {
        return new MySingletonBean();
    }
}

在上述配置中,scope="singleton" 或者通过Java配置中的@Bean注解,没有指定@Scope注解,默认就是单例作用域。

特点:

  1. 唯一实例: Spring容器中只会创建该Bean的一个实例,不论有多少次请求该Bean。

  2. 全局共享: 适用于那些可以在整个应用中共享的对象。

  3. 节省资源: 对于那些占用大量资源或创建耗时的对象,使用单例可以减少资源消耗。

需要注意的是,虽然单例Bean在大多数情况下是很有用的,但也要小心可能引发的线程安全问题,因为单例Bean是在整个应用中共享的。确保单例Bean的状态是线程安全的,或者采用适当的同步机制。

原型作用域

在Spring中,原型作用域(Prototype Scope)是一种Bean的作用域,它与单例作用域相反。当一个Bean被定义为原型时,每次从容器中获取Bean时,都会创建一个新的实例。这确保每个请求得到的是一个独立的、新的Bean对象。

如何声明原型Bean:

  1. 使用XML配置:
<bean id="myPrototypeBean" class="com.example.MyPrototypeBean" scope="prototype">
    <!-- 其他配置属性 -->
</bean>
  1. 使用Java配置:
@Configuration
public class AppConfig {

    @Bean
    @Scope("prototype")
    public MyPrototypeBean myPrototypeBean() {
        return new MyPrototypeBean();
    }
}

在上述配置中,scope="prototype" 或者通过Java配置中的@Scope("prototype") 注解都将Bean声明为原型作用域。

特点:

  1. 每次请求新实例: 每次从容器中获取Bean时都会创建一个新的实例。

  2. 适用于状态不可共享: 适用于那些Bean实例的状态不可共享,每个请求需要一个独立的实例的情况。

  3. 资源消耗较大: 适用于那些占用较多资源、创建较慢的对象。

需要注意的是,由于原型Bean的每次请求都会创建一个新实例,容器不会管理这些实例的生命周期。这意味着,如果原型Bean中有需要在销毁时执行的逻辑(例如关闭资源),你需要自行管理。

会话作用域

在Spring中,会话作用域(Session Scope)是一种特殊的作用域,适用于Web应用程序中。当一个Bean被定义为会话作用域时,Spring容器会为每个用户会话(HTTP会话)创建一个独立的实例。这样可以确保在同一会话中共享的Bean实例,而不同会话之间的实例是独立的。

如何声明会话作用域Bean:

  1. 使用XML配置:
<bean id="mySessionBean" class="com.example.MySessionBean" scope="session">
    <!-- 其他配置属性 -->
</bean>
  1. 使用Java配置:
@Configuration
public class AppConfig {

    @Bean
    @Scope("session")
    public MySessionBean mySessionBean() {
        return new MySessionBean();
    }
}

在上述配置中,scope="session" 或者通过Java配置中的 @Scope("session") 注解都将Bean声明为会话作用域。

特点:

  1. 每个会话创建一个实例: 对于Web应用中的每个用户会话,Spring容器会创建一个独立的实例。

  2. 适用于会话状态跟踪: 适用于那些需要跟踪用户会话状态的Bean,例如用户登录信息等。

  3. 与HTTP会话关联: 与HTTP会话的生命周期相对应,当用户会话结束时,与之关联的Bean实例也被销毁。

会话作用域的使用通常限定在Web应用中,因为它依赖于HTTP会话的存在。这种作用域是有状态的,因此在设计时需要注意确保Bean的状态在会话之间不会产生冲突。

请求作用域

在Spring中,请求作用域(Request Scope)是一种特殊的作用域,适用于Web应用程序中。当一个Bean被定义为请求作用域时,Spring容器会为每个HTTP请求创建一个独立的实例。这样可以确保在同一HTTP请求中共享的Bean实例,而不同请求之间的实例是独立的。

如何声明请求作用域Bean:

  1. 使用XML配置:
<bean id="myRequestBean" class="com.example.MyRequestBean" scope="request">
    <!-- 其他配置属性 -->
</bean>
  1. 使用Java配置:
@Configuration
public class AppConfig {

    @Bean
    @Scope("request")
    public MyRequestBean myRequestBean() {
        return new MyRequestBean();
    }
}

在上述配置中,scope="request" 或者通过Java配置中的 @Scope("request") 注解都将Bean声明为请求作用域。

特点:

  1. 每个HTTP请求创建一个实例: 对于Web应用中的每个HTTP请求,Spring容器会创建一个独立的实例。

  2. 适用于与单个请求相关的Bean: 适用于那些需要与单个HTTP请求相关的Bean,例如处理用户请求的控制器。

  3. 与HTTP请求关联: 与HTTP请求的生命周期相对应,当HTTP请求结束时,与之关联的Bean实例也被销毁。

请求作用域的使用通常限定在Web应用中,因为它依赖于HTTP请求的存在。这种作用域是有状态的,因此在设计时需要注意确保Bean的状态在请求之间不会产生冲突。

自定义作用域

在Spring中,你可以通过实现Scope接口来创建自定义的作用域。自定义作用域可以满足特定需求,而不局限于Spring提供的默认作用域(如单例、原型、请求、会话等)。

以下是一个简单的示例,展示如何实现自定义作用域:

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

import java.util.HashMap;
import java.util.Map;

public class CustomScope implements Scope {

    private final Map<String, Object> scopedObjects = new HashMap<>();
    private final Map<String, Runnable> destructionCallbacks = new HashMap<>();

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        if (!scopedObjects.containsKey(name)) {
            scopedObjects.put(name, objectFactory.getObject());

            // Register a destruction callback
            destructionCallbacks.put(name, () -> {
                // Your custom destruction logic here
                System.out.println("CustomScope: Destroying bean with name " + name);
            });
        }
        return scopedObjects.get(name);
    }

    @Override
    public Object remove(String name) {
        destructionCallbacks.remove(name);
        return scopedObjects.remove(name);
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        destructionCallbacks.put(name, callback);
    }

    @Override
    public Object resolveContextualObject(String key) {
        // Not used in this example
        return null;
    }

    @Override
    public String getConversationId() {
        // Not used in this example
        return null;
    }
}

在这个例子中,CustomScope实现了Scope接口,并提供了自定义的作用域逻辑。其中:

  • get: 获取指定名称的Bean实例,如果不存在,则创建一个新实例。
  • remove: 移除指定名称的Bean实例。
  • registerDestructionCallback: 注册销毁回调,当作用域结束时,Spring容器会调用这些回调以执行自定义的销毁逻辑。
  • resolveContextualObject: 用于解析上下文对象,通常不使用。
  • getConversationId: 用于获取对话标识,通常不使用。

接下来,你需要将自定义作用域注册到Spring容器中。这可以通过配置文件或Java配置类完成:

@Configuration
public class AppConfig {

    @Bean
    public static CustomScope customScope() {
        return new CustomScope();
    }

    @Bean
    @Scope(value = "customScope", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public MyCustomScopedBean myCustomScopedBean() {
        return new MyCustomScopedBean();
    }
}

在上述配置中,@Scope注解的value属性指定了使用的作用域,这里是customScope,而proxyMode属性设置为ScopedProxyMode.TARGET_CLASS,用于解决自定义作用域中的依赖注入问题。

请注意,这只是一个简单的自定义作用域示例,实际上可以根据具体需求实现更复杂的逻辑。

作用域的生命周期

在Spring中,不同作用域的Bean生命周期有着显著的差异。主要的作用域包括单例作用域(Singleton Scope)和原型作用域(Prototype Scope)。

单例作用域:

  1. 初始化: 单例Bean在容器启动时被创建(懒加载除外),并在整个应用程序的生命周期内保持不变。在首次请求该Bean时,Spring容器会调用构造函数、属性注入、初始化方法(如afterPropertiesSet@PostConstruct注解的方法)。

  2. 销毁: 单例Bean的销毁是在Spring容器关闭时进行的。当容器关闭时,Spring会调用单例Bean的销毁方法(如destroy@PreDestroy注解的方法)。

原型作用域:

  1. 初始化: 原型Bean在每次请求时都会创建一个新实例。每次请求该Bean时,Spring容器会调用构造函数、属性注入、初始化方法(如afterPropertiesSet@PostConstruct注解的方法)。

  2. 销毁: 原型Bean的销毁是由客户端代码负责的。Spring容器不会追踪原型Bean实例的生命周期,也不会在实例不再被引用时自动销毁。如果你的原型Bean实现了DisposableBean接口或使用了@PreDestroy注解的销毁方法,你需要手动调用这些方法。

影响:

  1. 资源消耗: 单例Bean由Spring容器管理,因此在整个应用程序生命周期内保持不变。这可能导致资源消耗较大。相反,原型Bean的资源消耗较小,因为每次请求都创建一个新实例。

  2. 状态共享: 单例Bean适用于需要在整个应用程序共享状态的情况,而原型Bean适用于需要保持独立状态的情况。

  3. 销毁控制: 单例Bean由Spring容器负责销毁,而原型Bean的销毁由客户端代码负责。

总的来说,选择单例作用域还是原型作用域取决于应用程序的具体需求。单例适用于共享状态的场景,而原型适用于每次请求都需要一个独立实例的场景。

实际应用场景

不同的作用域适用于不同的应用场景,以下是各个作用域的一些实际应用场景:

1. 单例作用域(Singleton Scope):

  • 共享状态的服务: 适用于需要在整个应用程序中共享状态的服务,例如配置管理服务、缓存服务等。

  • 工具类: 适用于那些无状态、只提供工具方法的类,因为这样的类在整个应用程序中可以被共享。

  • 线程安全的服务: 如果一个服务是线程安全的,并且需要在整个应用程序中被共享,那么可以考虑使用单例作用域。

2. 原型作用域(Prototype Scope):

  • 每次请求都需要一个新实例: 适用于那些在每次请求时都需要创建一个全新实例的Bean,例如Web应用中的表单处理器、控制器等。

  • 状态不可共享的Bean: 如果Bean的状态是不可共享的,且需要在每次请求中保持独立,可以考虑使用原型作用域。

  • 资源消耗较大的Bean: 对于创建耗时或占用大量资源的Bean,使用原型作用域可以避免在整个应用程序生命周期内占用过多资源。

3. 会话作用域(Session Scope):

  • 用户会话相关的Bean: 适用于需要在用户会话级别上共享状态的Bean,例如用户登录信息、购物车信息等。

  • 用户个性化设置: 如果有需要在用户会话级别上保存个性化设置的Bean,可以考虑使用会话作用域。

4. 请求作用域(Request Scope):

  • 每个HTTP请求需要一个新实例: 适用于那些与单个HTTP请求相关的Bean,例如处理用户请求的控制器、拦截器等。

  • 请求级别的数据: 如果有需要在请求级别上保存数据的Bean,可以考虑使用请求作用域。

  • 与请求相关的资源管理: 对于一些需要在请求结束时释放的资源,可以使用请求作用域,确保资源在请求完成后得到正确释放。

选择作用域时,需要根据具体的业务需求和Bean的性质来决定。合理使用不同的作用域有助于提高应用程序的性能和资源利用率。

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

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

相关文章

基于粒子群算法的参数拟合,寻优算法优化测试函数

目录 摘要 测试函数shubert 粒子群算法的原理 粒子群算法的主要参数 粒子群算法原理 粒子群算法参数拟合 代码 结果分析 展望 基于粒子群算法的参数拟合(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88698417 摘要 寻优算法,测试…

Node.js+Express+Mysql实现分页查询

根据记录数总数和分页数获到页总数 function pageCount (totalnum,limit){return totalnum > 0 ? ((totalnum < limit) ? 1 : ((totalnum % limit) ? (parseInt(totalnum / limit) 1) : (totalnum / limit))) : 0; } 接收请求代码 router.get(/api/user/page, asy…

iOS UITextField复制、粘贴框显示为英文如何解决

问题描述&#xff1a; 使用UITextField&#xff0c;欲粘贴文本&#xff0c;长按或者双击展示的提示框显示为英文 解决方案&#xff1a; 在Xcode配置文件info,plist文件中&#xff0c;新增Localizas属性&#xff0c;填入Chinese 结果如下&#xff1a; 提示框成功展示为中文

PostgreSQL 分区

由于大量数据存储在数据库同一张表中&#xff0c;后期性能和扩展会受到影响。所以需要进行表分区&#xff0c;因为它可以将大表分成较小的表&#xff0c;从而减少内存交换问题和表扫描&#xff0c;最终提高性能。庞大的数据集被分成更小的分区&#xff0c;更易于访问和管理。 …

系列十一、(一)Sentinel简介

一、Sentinel简介 1.1、官网 【英文文档】 https://github.com/alibaba/Sentinel/wiki【中文文档】 https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5 1.2、概述 1.3、功能

Matlab/F#/R进行数据分析和建模算法的经验,vb.net输给他了

微软放弃了vb.net的开发&#xff0c;但是持续花费巨资投入F#,简单一看他的语法就是qbasic ,vb6一样。鹿死谁手&#xff0c;谁能相信vb.net竟然被f#给干掉了。外面有vb6语法的python成了全球第一的编程语言,内部还有强大的教授开发的这工具扯后腿。 有人说为什么中国搞不出像mat…

【数据结构】链表简介及单链表的实现

简单不先于复杂&#xff0c;而是在复杂之后。 文章目录 1. 链表1.1 链表的概念及结构1.2 链表的分类1.3 无头单向非循环链表的实现 1. 链表 1.1 链表的概念及结构 概念&#xff1a;链表是一种物理存储结构上非连续&#xff0c;非顺序的存储结构&#xff0c;数据元素的逻辑顺序…

Jvm垃圾收集器系列之Parallel Scavenge收集器(个人见解仅供参考)

问&#xff1a;什么是Parallel Scavenge&#xff1f; 答&#xff1a;Parallel Scavenge是Java HotSpot虚拟机中的一种垃圾收集器&#xff0c;它主要用于提高应用程序的吞吐量。 问&#xff1a;Parallel Scavenge的主要目标是什么&#xff1f; 答&#xff1a;Parallel Scavenge的…

Hive - Select 使用 in 限制范围

目录 一.引言 二.Select Uid Info 1.少量 Uid 2.大量 Uid ◆ 建表 ◆ 本地 Load ◆ HDFS Load ◆ Select In 三.总结 一.引言 工业场景下 Hive 表通常使用 uid 作为用户维度构建和更新 Hive 表&#xff0c;当我们需要查询指定批次用户信息时&#xff0c;可以使用 in …

自制Java镜像发布到dockerhub公网使用

文章目录 问题现象解决制作Java镜像发布使用 问题现象 书接上回&#xff0c;上周处理了一个docker问题&#xff0c;写了篇博客&#xff1a;自定义docker镜像&#xff0c;ubuntu安装命令并导出我们使用谷歌的jib插件打包&#xff0c;详情可以参考这篇文章&#xff1a;Spring Bo…

unity C#中Array、Stack、Queue、Dictionary、HashSet优缺点和使用场景总结

文章目录 数组 (Array)列表 (List<T>)栈 (Stack<T>)队列 (Queue<T>)链表 (LinkedList<T>)哈希表 (Dictionary<TKey, TValue>) 或 HashSet<T>集合 (Collection<T>) 数组 (Array) 优点&#xff1a; 高效访问&#xff1a;通过索引可以…

uniCloud 云函数

相对于云函数&#xff0c;官方更推荐使用 云对象 新建云函数 编辑云函数 uniCloud-aliyun/cloudfunctions/hello_func/index.js use strict; exports.main async (event, context) > {let {name} eventreturn 你好&#xff0c;${name}! };云函数接收的参数从event中解构获…

二进制安装包安装Prometheus插件安装(mysql_exporter)

简介 mysql_exporter是用来收集MysQL或者Mariadb数据库相关指标的&#xff0c;mysql_exporter需要连接到数据库并有相关权限。既可以用二进制安装部署&#xff0c;也可以通过容器形式部署&#xff0c;但为了数据收集的准确性&#xff0c;推荐二进制安装。 一&#xff0c;下载安…

“百模大战:AI行业的技术竞争与发展趋势“

文章目录 每日一句正能量前言百模大战影响推理配置后记 每日一句正能量 修行不是抬起头朝天上看而是低下头学会谦卑和诚实。 前言 到目前为止&#xff0c;如果要评选2023年热度最高&#xff0c;影响力最大的一个概念&#xff0c;当属AI大模型了。这轮由ChatGPT引爆的技术热潮&…

K8S--部署SpringBoot项目实战

原文网址&#xff1a;K8S--部署SpringBoot项目实战-CSDN博客 简介 本文介绍K8S如何部署SpringBoot项目。 1.生成应用的docker镜像 把SpringBoot项目的jar包打包为docker镜像&#xff0c;见&#xff1a;Docker Compose--部署SpringBoot项目--实战-CSDN博客 创建后的镜像名称…

论文悦读(7)——NVM文件系统之Trio(SOSP‘23)文件系统

TRIO&#xff08;SOSP23&#xff09; 1. 背景&#xff08;Background&#xff09;1.1 NVM Technologis1.2 File System Customization1.3 Userspace NVM File Systems 2. 观察与动机&#xff08;Observation & Motivation&#xff09;3. 设计与实现&#xff08;Design &…

(18)Linux 实现简易版shell

前言&#xff1a;做一个 "会创建&#xff0c;会终止&#xff0c;会等待&#xff0c;会程序替换" 的简易 shell 。 1、显示提示符和获取用户输入 shell 本质就是个死循环&#xff0c;我们不关心获取这些属性的接口&#xff0c;如果要实现 shell&#xff1a; 1&…

conda环境下Could not create share link解决方法

1 问题描述 在运行chatglm-6B项目时&#xff0c;运行python web_demo.py&#xff0c;出现如下错误&#xff1a; (chatglm) [rootlocalhost ChatGLM2-6B]# python web_demo.py Loading checkpoint shards: 100%|██████████████████████████████…

scratch给数据清单排序 2023年12月中国电子学会图形化编程 少儿编程 scratch编程等级考试四级真题和答案解析

目录 scratch给数据清单排序 一、题目要求 1、准备工作 2、功能实现 二、案例分析

性能优化-OpenMP基础教程(三)-Android上运行OpenMP

本文主要介绍如何在一个常规的Android手机上调试OpenMP程序&#xff0c;包括Android NDK的环境配置和使用JNI编写一个OpenMP程序运行在Android手机中。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#…