解决 Spring 单例 Bean 注入原型 Bean 被共享的问题

在使用 Spring 开发应用时,Bean 的作用域(Scope) 决定了 Bean 实例的生命周期。Spring 提供了多种作用域,其中最常见的是 单例(singleton) 和 原型(prototype) 作用域。

  • 单例作用域(singleton):容器中只有一个实例,整个应用上下文中的 Bean 共享同一个实例。
  • 原型作用域(prototype):每次请求都会创建一个新的实例,每个请求返回一个不同的对象。

理论上,原型 Bean 的每次请求都应返回一个新的实例,而单例 Bean 应该只存在一个实例。问题发生在当 原型 Bean 注入到单例 Bean 中时,原型 Bean 的实例化行为可能并不像预期的那样,每次都返回新的实例。

一、问题描述

假设我们有一个 单例 Bean SingletonBean,它需要使用一个 原型 Bean PrototypeBean 来完成某些功能。通常,我们期望每次调用 SingletonBean 中的 PrototypeBean 时,都能获得一个新的实例。然而,由于 Spring 的默认行为,PrototypeBean 在 SingletonBean 中可能会被共享,这与我们希望原型 Bean 每次创建新实例的设计相冲突。

示例代码
@Component
@Scope("singleton")  // SingletonBean 是单例作用域
public class SingletonBean {@Autowiredprivate PrototypeBean prototypeBean; // 注入原型 Beanpublic void doSomething() {prototypeBean.doSomething();  // 每次都会调用同一个实例}
}@Component
@Scope("prototype")  // PrototypeBean 是原型作用域
public class PrototypeBean {public void doSomething() {System.out.println("PrototypeBean doing something");}
}

在这个示例中:

  • SingletonBean 是单例作用域的,每次访问都会返回相同的实例。
  • PrototypeBean 是原型作用域的,本应每次请求时创建新的实例,但由于它被注入到 SingletonBean 中,Spring 只会在容器启动时实例化一次 PrototypeBean,并将该实例注入到 SingletonBean 中。

问题的本质是:尽管 PrototypeBean 是原型作用域的,Spring 会在 SingletonBean 创建时注入它的实例,并且在 SingletonBean 的整个生命周期内使用同一个 PrototypeBean 实例,而不会每次都创建新的实例。

二、问题产生的原因

问题的根源在于 Spring 的依赖注入机制:

  1. Spring 容器在启动时创建所有的 Bean。对于单例作用域的 Bean,Spring 会在容器初始化时创建实例,并且在整个应用生命周期内保持该实例。而原型作用域的 Bean 每次被请求时都会创建新实例。

  2. 单例 Bean 中注入原型 Bean 时,Spring 会在容器初始化时就实例化 PrototypeBean,并将其注入到 SingletonBean 中。即使 PrototypeBean 是原型作用域的,它的实例也会在容器初始化时就被创建,并且这个实例会在 SingletonBean 的整个生命周期内被共享。

  3. 没有延迟加载机制:单例 Bean 中的 PrototypeBean 注入后,Spring 并不会每次重新创建一个新的实例,而是使用已经注入的原型 Bean 实例。因此,每次访问 SingletonBean 时,得到的 PrototypeBean 实例是同一个,而不是一个新的实例。

三、解决方案

为了确保 单例 Bean 中注入的原型 Bean 每次都能返回新的实例,可以通过以下几种方法来解决这个问题。

1. 使用 ObjectProvider

ObjectProvider 是 Spring 提供的一种 延迟加载机制。通过 ObjectProvider,我们可以在需要时动态地获取原型 Bean,确保每次调用时都返回一个新的实例。

@Component
public class SingletonBean {@Autowiredprivate ObjectProvider<PrototypeBean> prototypeBeanProvider;public void doSomething() {// 每次调用 getObject() 时都会获取新的 PrototypeBean 实例PrototypeBean prototypeBean = prototypeBeanProvider.getObject();prototypeBean.doSomething();}
}
  • ObjectProvider<PrototypeBean> 让 PrototypeBean 以延迟加载的方式实例化,每次调用 getObject() 时都会返回新的实例。
  • 这种方法可以确保每次调用时都获得一个新的原型 Bean 实例,而不是复用已注入的实例。
2. 使用 ApplicationContext 获取原型 Bean

ApplicationContext 提供了一个方法 getBean(),可以动态获取原型作用域的 Bean。通过这种方式,我们可以确保每次获取原型 Bean 时,都会得到一个新的实例。

@Component
public class SingletonBean {@Autowiredprivate ApplicationContext applicationContext;public void doSomething() {// 每次调用 getBean() 都会获取新的 PrototypeBean 实例PrototypeBean prototypeBean = applicationContext.getBean(PrototypeBean.class);prototypeBean.doSomething();}
}
  • applicationContext.getBean(PrototypeBean.class) 每次调用时都会返回新的 PrototypeBean 实例,而不是使用已经注入的实例。
  • 这种方法适用于需要通过 ApplicationContext 动态获取原型 Bean 的场景。
3. 结合 @Scope("prototype") 和 @Lazy 注解

@Lazy 注解可以延迟 Bean 的初始化,确保原型 Bean 只在第一次需要时才会被创建。这结合 @Scope("prototype") 注解可以确保每次请求时都会创建新的实例。

package com.nbsaas.boot.controller.web;import com.nbsaas.boot.rest.response.ResponseObject;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class StateController {private Integer account=0;@Resourceprivate  ObjectProvider<StateHandle> stateHandles;@Lazy@Resourceprivate StateHandle stateHandle;@RequestMapping("/state")public ResponseObject<String> state(){ResponseObject<String> result=new ResponseObject<>();account++;stateHandles.getObject().handle();stateHandle.handle();return result;}@RequestMapping("/account")public String account(){return "account:"+account;}@PostConstructpublic void init(){System.out.println("init");}}

当我们在 Spring 中遇到 单例 Bean 注入原型 Bean 被共享的问题时,问题的根源在于 Spring 默认会在容器启动时实例化原型 Bean,并将同一个实例注入单例 Bean。为了解决这个问题,我们可以使用以下几种方法:

  1. 使用 ObjectProvider:延迟加载原型 Bean,每次调用时返回新的实例。
  2. 使用 ApplicationContext:通过 getBean() 动态获取原型 Bean,每次获取新的实例。
  3. 结合 @Scope("prototype") 和 @Lazy 注解:延迟初始化原型 Bean,确保每次请求时创建新的实例。

这些解决方案能确保单例 Bean 中的原型 Bean 每次都创建新的实例,而不是共享同一个实例,从而解决了原型 Bean 被共享的问题。

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

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

相关文章

解决 OpenCV 与 FFmpeg 版本不兼容导致的编译错误

解决 OpenCV 与 FFmpeg 版本不兼容导致的编译错误 在安装并编译 OpenCV 3.2.0 版本时&#xff0c;出现的编译错误主要是由于 OpenCV 代码中使用的 FFmpeg 库宏定义与最新版 FFmpeg 库中的定义不一致所致。具体来说&#xff0c;原有的宏 CODEC_FLAG_GLOBAL_HEADER 和 AVFMT_RAW…

剑指offer搜索二维矩阵

题目连接 https://leetcode.cn/problems/search-a-2d-matrix-ii/’ 代码 自己想出来的 解法一 初始化两个指针&#xff0c;i0,j列数-1 若此时matrix[i][j]target 则返回true 若此时matrix[i][j]>target,表明在第j列中不可能存在target&#xff0c;因为列是升序的 若此时ma…

无管理员权限 LCU auth-token、port 获取(全网首发 go)

一&#xff1a; 提要&#xff1a; 参考项目&#xff1a; https://github.com/Zzaphkiel/Seraphine 想做一个 lol 查战绩的软件&#xff0c;并且满足自己的需求&#xff08;把混子和大爹都表示出来&#xff09;&#xff0c;做的第一步就是获取 lcu token &#xff0c;网上清一色…

STM32F407ZGT6-UCOSIII笔记6:UCOS-III软件定时器

今日学习使用UCOS系统的软件定时器功能 本文学习与程序编写基于 正点原子的 STM32F1 UCOS开发手册 文章提供测试代码讲解、完整工程下载、测试效果图 软件定时器卡柱UCOS III 系统问题解决 目录 UCOS-III 软件定时器&#xff1a; 单次定时器&#xff1a; 周期定时器: 开启软…

【网络安全】WIFI WPA/WPA2协议:深入解析与实践

WIFI WPA/WPA2协议&#xff1a;深入解析与实践 1. WPA/WPA2 协议 1.1 监听 Wi-Fi 流量 解析 WPA/WPA2 的第一步是监听 Wi-Fi 流量&#xff0c;捕获设备与接入点之间的 4 次握手数据。然而&#xff0c;设备通常不会频繁连接或重新连接&#xff0c;为了加速过程&#xff0c;攻…

【ubuntu18.04】ubuntu18.04挂在硬盘出现 Wrong diagnostic page; asked for 1 got 8解决方案

错误日志 [ 8754.700227] usb 2-3: new full-speed USB device number 3 using xhci_hcd [ 8754.867389] usb 2-3: New USB device found, idVendor0e0f, idProduct0002, bcdDevice 1.00 [ 8754.867421] usb 2-3: New USB device strings: Mfr1, Product2, SerialNumber0 [ 87…

金碟中间件-AAS-V10.0安装

金蝶中间件AAS-V10.0 AAS-V10.0安装 1.解压AAS-v10.0安装包 unzip AAS-V10.zip2.更新license.xml cd /root/ApusicAS/aas# 这里要将license复制到该路径 [rootvdb1 aas]# ls bin docs jmods lib modules templates config domains …

Reactor 响应式编程(第四篇:Spring Security Reactive)

系列文章目录 Reactor 响应式编程&#xff08;第一篇&#xff1a;Reactor核心&#xff09; Reactor 响应式编程&#xff08;第二篇&#xff1a;Spring Webflux&#xff09; Reactor 响应式编程&#xff08;第三篇&#xff1a;R2DBC&#xff09; Reactor 响应式编程&#xff08…

【Qt】信号、槽

目录 一、信号和槽的基本概念 二、connect函数&#xff1a;关联信号和槽 例子&#xff1a; 三、自定义信号和槽 1.自定义槽函数 2.自定义信号函数 例子&#xff1a; 四、带参的信号和槽 例子&#xff1a; 五、Q_OBJECT宏 六、断开信号和槽的连接 例子&#xff1a; …

数据库密码加密

数据库密码加密 简单来说&#xff1a; 我们会经常看到重置密码&#xff0c;小时候就会有疑惑&#xff0c;为什么不直接告诉我们密码&#xff0c;原来服务器自己也不知道。 我们都知道密码在数据库中不能明文&#xff0c;不然风险很高&#xff0c;有数据库权限的人还可能恶意利…

PCIE概述

PCIE概述 文章目录 PCIE概述前言一、应用场景二、PCIE理论2.1 硬件2.2 拓扑结构&#xff1a;处理器和设备之间的关系2.3 速率2.4 层次接口2.5 四种请求类型2.5.1 bar空间2.5.2 memory2.5.3 IO2.5.4 configuration2.5.5 message 前言 参考链接&#xff1a; pcie总线知识点解析 …

Android Studio创建新项目并引入第三方so外部aar库驱动NFC读写器读写IC卡

本示例使用设备&#xff1a;https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.52de2c1bbW3AUC&ftt&id615391857885 一、打开Android Studio,点击 File> New>New project 菜单&#xff0c;选择 要创建的项目模版&#xff0c;点击 Next 二、输入项目名称…

NX系列-使用 `nmcli` 命令创建 Wi-Fi 热点并设置固定 IP 地址

使用 nmcli 命令创建 Wi-Fi 热点并设置固定 IP 地址 一、前言 在一些场景下&#xff0c;我们需要将计算机或嵌入式设备&#xff08;例如 NVIDIA Orin NX&#xff09;转换为 Wi-Fi 热点&#xff0c;以便其他设备&#xff08;如手机、笔记本等&#xff09;能够连接并使用该设备…

AngularJS 与 SQL 的集成应用

AngularJS 与 SQL 的集成应用 引言 在当今的Web开发领域,AngularJS 和 SQL 是两种非常重要的技术。AngularJS,作为一个强大的前端框架,能够帮助开发者构建复杂且高性能的客户端应用。而SQL(Structured Query Language),作为一种广泛使用的数据库查询语言,是管理关系型…

用docker快速安装电子白板Excalidraw绘制流程图

注&#xff1a;本文操作以debian12.8 最小化安装环境为host系统。 一、彻底卸载原有的残留 apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras 二、设置docker的安装源 # Add Dockers official G…

C++趟坑学习-new,delete,虚析构函数

#include <iostream> using namespace std;class Resource { public:Resource() { cout << "Resource constructed" << endl; }~Resource() { cout << "Resource destructed" << endl; } };int main() {// 动态分配一个包含…

Spring Mvc面试题(常见)

1 Spring MVC的执行流程 用户发起请求,请求先被Servlet拦截以后,转发给SpringMVC框架SpringMVC 里面的DispatcherServlet(核心控制器) 接收到请求,并转发给HandlerMappingHandlerMapping负责解析请求,根据请求信息和配置信息找到匹配的Controller类(当这里有配置拦截器,会…

mac 如何开启指定端口供外部访问?

前言 需要 mac 上开放指定端口&#xff0c;指定 ip 访问 解决 在 macOS 上开放一个端口&#xff0c;并指定只能特定的 IP 访问&#xff0c;可以使用 macOS 内置的 pfctl(Packet Filter)工具来实现。 1、 编辑 pf 配置文件&#xff1a; 打开 /etc/pf.conf 文件进行编辑。 可以使…

如何设置Jsoup解析京东商品详情?

在数字化时代&#xff0c;数据的价值日益凸显&#xff0c;尤其是在电商领域。通过爬虫技术&#xff0c;我们可以从网站中提取有价值的信息&#xff0c;用于市场分析、价格监控等。Java作为一种成熟且功能强大的编程语言&#xff0c;拥有丰富的库支持&#xff0c;使其成为编写爬…

STM32读写flash注意事项

STM32读写Flash时,需要注意以下事项以确保操作的正确性和可靠性: 一、写入操作注意事项 擦除操作: STM32内置Flash的写入操作必须遵循“先擦除,后写入”的原则。擦除操作以页(或扇区)为单位进行,这意味着在写入新数据之前,需要擦除整个页(或扇区)。写入单位: 写入操…