当前位置: 首页 > news >正文

Spring的Bean原型模式下的使用

目录

1、问题原因

1.1、注入点只初始化一次

1.2、代理模式问题

1.3、使用不当的获取方式

2、解决方案

2.1. ApplicationContext获取

2.2. 使用ObjectProvider

2.3. 使用Lookup方法

2.4. 使用Provider接口

3、原型模式的应用场景

1. 有状态的Bean

2. 线程不安全的对象

3. 需要每次使用新实例的场景

4. 需要避免副作用的场景

4、注意事项


前言

        原型(Prototype)模式的Bean在Spring中每次请求时都会创建一个新的实例,这与单例(Singleton)模式形成对比。

如下图所示:

        默认情况下,Spring中的Bean采用单例模式,即容器始终返回同一实例。然而,通过将singleton属性设置为false,可以使得每次请求都返回一个新的Bean实例。

        当你在Spring中使用原型(prototype)作用域的Bean时,如果通过@Autowired注入后发现仍然是单例行为。


1、问题原因

1.1、注入点只初始化一次

@Autowired在依赖注入时只发生一次(在Bean创建时),之后每次访问都是同一个实例。

1.2、代理模式问题

        如果原型Bean被代理(如AOP代理),而代理配置不正确,可能导致每次获取的都是同一个代理实例。

1.3、使用不当的获取方式

直接通过@Autowired注入原型Bean实际上违背了原型模式的设计初衷

总结:

  @Autowired在依赖注入时只执行一次,将原型Bean注入到单例Bean中时,这个注入动作只发生一次,因此单例Bean持有的始终是同一个原型Bean实例。这与原型Bean的设计初衷相违背。

        正确的方式应该是每次需要新实例时都从容器中显式获取,而不是在初始化时就注入并持有。


2、解决方案

2.1. ApplicationContext获取

@Autowired
private ApplicationContext applicationContext;public void someMethod() {PrototypeBean bean = applicationContext.getBean(PrototypeBean.class);// 每次调用getBean()都会返回新实例
}

2.2. 使用ObjectProvider

使用(Spring 4.3+)版本,代码示例如下:

@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;public void someMethod() {PrototypeBean bean = prototypeBeanProvider.getObject();// 每次getObject()都会返回新实例
}

2.3. 使用Lookup方法

通过@lookup注解,重写get${method}方法。

代码示例如下:

@Configuration
public class AppConfig {@Bean@Scope("prototype")public PrototypeBean prototypeBean() {return new PrototypeBean();}
}@Component
public class SingletonBean {// 使用方法注入@Lookuppublic PrototypeBean getPrototypeBean() {return null; // Spring会覆盖此方法实现}public void someMethod() {PrototypeBean bean = getPrototypeBean(); // 每次调用都返回新实例}
}

2.4. 使用Provider接口

Provider接口是位于(javax.inject)包下。

代码实现如下:

@Autowired
private Provider<PrototypeBean> prototypeBeanProvider;public void someMethod() {PrototypeBean bean = prototypeBeanProvider.get();// 每次get()都会返回新实例
}

3、原型模式的应用场景

1. 有状态的Bean

当Bean需要维护状态,且不同使用者需要独立的状态时:

  • 用户会话相关的对象

  • 请求处理过程中的上下文对象

  • 购物车实例

  • 表单数据对象

@Scope("prototype")
@Component
public class ShoppingCart {private List<Item> items = new ArrayList<>();// 每个用户需要自己的购物车实例
}

2. 线程不安全的对象

当Bean不是线程安全的,且需要在多线程环境中使用时:

  • 简单的日期格式化器(SimpleDateFormat)

  • 随机数生成器

  • 某些第三方库的非线程安全类

@Scope("prototype")
@Component
public class DateFormatter {private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");// 每个线程需要自己的实例
}

3. 需要每次使用新实例的场景

  • 临时计算器对象

  • 事务处理器(某些场景下)

  • 原型设计模式的实际应用

@Scope("prototype")
@Component
public class ReportGenerator {// 每次生成报告都需要新的实例来保持独立性
}

4. 需要避免副作用的场景

当Bean的操作会修改内部状态,且这种修改不应该影响其他使用者时:

  • 数据处理器

  • 文件处理器

  • 临时缓存对象

@Scope("prototype")
@Component
public class FileProcessor {private File currentFile;// 每个文件处理需要独立的处理器实例
}

4、注意事项

  1. 内存考虑:原型Bean不会自动销毁,需要确保不会造成内存泄漏

  2. 性能影响:频繁创建复杂对象可能影响性能

  3. 依赖管理:原型Bean注入单例Bean时要特别小心(通常使用方法注入)

  4. 测试复杂性:原型Bean可能增加测试的复杂性

        正确使用原型模式可以解决许多与状态和线程安全相关的问题,但需要权衡其带来的资源开销。

http://www.dtcms.com/a/268727.html

相关文章:

  • OpenWebUI(3)源码学习-后端models数据模型模块
  • 【论文撰写】如何把AI生成的文本公式复制在word中,完整的复制公式,拷贝豆包生成的公式
  • 网络安全之注入攻击:原理、危害与防御之道
  • 文件IO day29
  • 代码幽灵5-终局:静默编译
  • Baklib: 用 “技术轻量化” 解决内容管理 “重需求”
  • Linux命令的命令历史
  • 大数据在UI前端的应用创新:基于情感计算的用户体验优化
  • 冠能高端系列真实口碑如何
  • TCP backlog工作机制
  • AI时代,传统票务系统该往哪里使劲?
  • 华为手机如何扫描到SLE设备
  • 如何备份vivo手机中的联系人?
  • “猫攻击”揭示推理模型脆弱性,凸显上下文工程的重要性
  • 存储器介绍
  • React16,17,18,19新特性更新对比
  • 面向智驾的车规级高精度RTK模块UM680A的引脚功能
  • Git在Pycharm中的使用
  • web网页开发,在线%ctf管理%系统,基于html,css,webform,asp.net mvc, sqlserver, mysql
  • 【论文阅读】SASLN:小样本条件下机械故障诊断的信号增强自学习网络
  • Redis常用数据结构以及多并发场景下的使用分析:Set类型
  • react状态管理库 - zustand
  • BitMart“滑点守护计划”二期重磅升级,定义安心交易新纪元
  • Redis哨兵模式之Sentinel模式(二)
  • vue3 强制刷新 forceUpdate
  • 关于使用shiro中Session的使用导致的Java 对象引用问题
  • 【BTC】比特币系统的具体实现
  • 《30天打牢数模基础-第一版》(已完结) 需要自取
  • 浅析德语OCR技术的实现难点及其工作原理
  • 怎么删除音频空白部分_去掉mp3空白部分