prototype 注入到 singleton 里,prototype是否还是线程安全的

"如果 prototype Bean 内部没有共享状态,自身是线程安全的。但 Spring 容器对 prototype 的多例行为有'陷阱',可能导致实际使用中不安全。"

展开讲:

prototype 注入到 singleton 里的线程安全分析

一、prototype Bean 自身的线程安全性

1.1 prototype 本身是"独立实例"(这个是安全的
复制代码
@Service@Scope("prototype")public class ShoppingCart {    private List<Item> items = new ArrayList<>();        public void addItem(Item item) {        items.add(item);  // 操作的是自己的成员变量    }}

为什么自身是线程安全的?

特性 解释
每个 prototype 实例独立 1000 个调用 → 1000 个 ShoppingCart 实例,互相不共享
每个实例的成员变量独立 线程 A 操作 cart1.items,线程 B 操作 cart2.items,互不干扰
GC 独立 一个实例的 GC 不影响其他实例

**所以 prototype Bean 本身是线程安全的,前提是你每次都拿到了不同的实例

1.2 但这里有"陷阱"------老哥 7+ 年必须知道

如果不加 proxyMode,singleton 里的 prototype 不是"真多例"

复制代码
@Service  // singletonpublic class OrderService {        @Autowired    private ShoppingCart cart;  // ⚠️ 看着像 prototype}

实际行为:

  • OrderService 是单例,整个应用只有 1 个
  • cartOrderService 创建时注入一次
  • cart 永远是同一个 ShoppingCart 实例!
  • 1000 个请求都共享同一个 cart → 多线程同时改 cart.items → 线程不安全!

二、3 种场景的线程安全分析

场景 1:纯 prototype(不注入到 singleton,直接 getBean
复制代码
ApplicationContext ctx = ...;ShoppingCart cart1 = ctx.getBean(ShoppingCart.class);  // 实例 1ShoppingCart cart2 = ctx.getBean(ShoppingCart.class);  // 实例 2(不同对象)

线程安全:✅ 是

  • 每次 getBean 都返回新实例
  • 多个线程拿到不同实例
  • 互不干扰
场景 2:prototype 注入到 singleton(不加 proxyMode
复制代码
@Service  // singletonpublic class OrderService {    @Autowired    private ShoppingCart cart;  // 注入时拿到一次}

线程安全:❌ 否

  • 整个应用 1 个 OrderService
  • 整个应用 1 个 cart(注入时定下来)
  • 多线程共享同一 cart → 不安全
场景 3:prototype + proxyMode / @Lazy(真正多例
复制代码
@Service@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,        proxyMode = ScopedProxyMode.TARGET_CLASS)public class ShoppingCart { ... }@Servicepublic class OrderService {    @Autowired    private ShoppingCart cart;  // 代理对象}

线程安全:✅ 是

  • 注入的是代理对象
  • 每次调用 cart 方法时,代理内部创建一个新的 ShoppingCart
  • 1000 个请求 → 1000 个不同 ShoppingCart → 互不干扰

三、Spring 多例的 4 个"坑"(老哥面试加分

坑 1:注入时拿到一次(最常踩
复制代码
@Servicepublic class A {    @Autowired    private PrototypeBean b;  // ⚠️ 整个 A 共享同一个 b}

解决: proxyMode = TARGET_CLASS@Lazy

坑 2:@Async 注解的方法内 prototype 失效
复制代码
@Service@Scope("prototype")public class TaskRunner { ... }@Servicepublic class TaskService {    @Autowired    private TaskRunner runner;        @Async  // 异步调用    public void run() {        // 这里 runner 是多少个?看注入方式    }}

坑:

  • 如果 @Autowired 直接注入,runner 永远同一个
  • 必须用 ObjectProvider<TaskRunner> 每次 .getObject() 才拿到新实例
坑 3:prototype 在 @Configuration 里 @Bean 不会自动多例
复制代码
@Configurationpublic class AppConfig {        @Bean    @Scope("prototype")    public ShoppingCart cart() {        return new ShoppingCart();    }}

坑:

  • 这种写法是真的多例(每次 getBean 都新建)
  • 调用 cart() 方法本身只返回同一个对象(因为 Spring 拦截了 @Bean 方法)
  • 实际多例要靠其他 Bean 注入或 getBean 触发
坑 4:prototype Bean 的销毁不归 Spring 管
复制代码
@PreDestroy  // ⚠️ prototype Bean 上加这个不会生效public void cleanup() {    // 永远不会调用}

解决:

  • 实现 DisposableBean 接口
  • BeanPostProcessor 手动管理

四、面试官追问应对

追问:prototype 注入到 singleton 里,prototype 还线程安全吗?

"分情况看

如果用 proxyMode / @Lazy 真正多例:✅ 线程安全(每个线程拿到不同实例)。

如果直接 @Autowired 不加处理:❌ 不安全(整个应用共享同一个 prototype 实例)。

核心坑:singleton Bean 创建时 prototype 注入一次,多线程共享。

追问 2:怎么判断当前 Bean 是不是 prototype?
复制代码
// 运行时判断if (ctx.containsBean("xxx") && ctx.isPrototype("xxx")) {    // 是 prototype}
追问 3:怎么让 singleton 注入 prototype 真的多例?

"3 种方法

1.@Scope + proxyMode = TARGET_CLASS(最推荐)

2.@Lazy(延迟加载,代理对象)

3.ObjectProvider<PrototypeBean> + .getObject()最灵活)"

追问 4:ObjectProvider 怎么用?
复制代码
@Servicepublic class OrderService {        @Autowired    private ObjectProvider<ShoppingCart> cartProvider;  // 不直接注入        public void checkout() {        ShoppingCart cart = cartProvider.getObject();  // 每次调用拿到新实例        cart.addItem(...);    }}

ObjectProvider 优点:

  • 显式控制获取时机
  • 不用加 @Scope / @Lazy / proxyMode
  • 项目里推荐用这个(最清晰)

六、面试答法模板

"3 句话讲清楚 prototype 线程安全

1.prototype 本身是独立的 (每个实例不共享成员变量),所以自身线程安全

2.但注入到 singleton Bean 里时,不加 proxyMode / @Lazy 会变成'假多例' (整个应用共享同一实例),多线程并发改这个共享实例就线程不安全

3.**正确做法:proxyMode = TARGET_CLASS / @Lazy / ObjectProvider,**保证每次调用拿到新实例。

我做的 MOVA 报表生成器就是这个套路,proxyMode 让 100 个并发任务互不干扰。"

七、一句话总结

"prototype 自身线程安全,但注入到 singleton 不加 proxyMode 会变成'假多例',整个应用共享一个 prototype 实例,反而不安全。"

相关推荐
Suhan422 小时前
IDEA 设置启动参数和环境变量
spring boot·spring·编辑器·idea
川石课堂软件测试2 小时前
安全测试|常见SQL注入攻击方式、实例及预防
服务器·数据库·sql·功能测试·测试工具·安全·单元测试
上海云盾-小余2 小时前
中小站点防护避坑:低价高防服务存在的各类安全短板剖析
网络·爬虫·安全·ddos
武子康2 小时前
从恶意 Dataset 到横向移动:一套可落地的 Dataset Processing Threat Model(资产边界 + 威胁枚举 + 5 层控制)
人工智能·安全
羽翼安全3 小时前
企业内网终端可视化风控方案解析,电脑屏幕防泄露系统保障数据资产安全
安全
数据知道4 小时前
邮件安全实战:SPF/DKIM/DMARC 配置与钓鱼绕过
网络·安全·web安全·网络安全·邮件安全
橘子海全栈攻城狮5 小时前
【最新源码】基于SpringBoot + Vue的超市管理系统的设计与实现D002
java·开发语言·vue.js·spring boot·后端·spring
qizayaoshuap5 小时前
# [特殊字符] 密码生成器 — 鸿蒙ArkTS安全算法与密码强度评估系统
java·算法·安全·华为·harmonyos
空中湖5 小时前
Spring AI Function Calling 完全指南:让 AI 查数据库、调 API、发通知
数据库·人工智能·spring
风起洛阳@不良使5 小时前
spring中xml和注解开发的对比
xml·java·spring