Skip to content

P64 别再误解了,Spring 的单例是容器视角,不是 JVM 视角 ​

面试题:Spring 的单例 Bean 和"单例模式"是同一个概念吗?

1. 核心结论 ​

不是一回事。 Spring 的单例是容器视角:同一个 Spring 容器内,该 Bean 只有一个实例;但同一个 JVM 里可以有多个 Spring 容器,也可以有多个该类的实例。

2. 对比 ​

对比项单例模式(GoF)Spring 单例 Bean
作用范围整个 JVM 一个实例每个容器一个实例
实现方式私有构造器 + 静态方法容器管理,默认 scope=singleton
多个实例可能不可能(构造器私有)可能(多个容器/多份 classloader)
控制权类自己控制容器控制生命周期

3. 为什么说"容器视角" ​

java
AnnotationConfigApplicationContext ctx1 = new AnnotationConfigApplicationContext(AppConfig.class);
AnnotationConfigApplicationContext ctx2 = new AnnotationConfigApplicationContext(AppConfig.class);

ctx1.getBean(UserService.class) == ctx1.getBean(UserService.class);  // true,同一容器内单例
ctx1.getBean(UserService.class) == ctx2.getBean(UserService.class);  // false,不同容器不同实例

另外:

  • 一个 JVM 多个 Spring 容器很常见:Spring Boot 主容器 + 测试容器 + 插件/模块容器;
  • 多个 classloader(如 Tomcat 多个 Web 应用)各加载一份类,即使都叫 singleton 也不是同一个对象;
  • 集群部署时,每个 JVM 各有一个容器 → 每个 JVM 各有一份 Bean 实例。

4. 影响与注意点 ​

正因为是"每容器单例":

  • Bean 里的状态(成员变量)是容器内共享的,多线程要小心线程安全(如无状态 Service 最安全);
  • 别假设"全局唯一":跨容器拿不到对方的 Bean;
  • 分布式/多实例部署下,Bean 内的本地状态不共享,需要外部存储。

5. 加分点 ​

  • 面试官追问"那 Spring 单例 Bean 线程安全吗":单例 ≠ 线程安全,取决于 Bean 是否有可变状态;
  • 主动提到 scope 全家桶:singleton / prototype / request / session / application;
  • 提到"Spring 单例缓存":DefaultSingletonBeanRegistry 的单例池(三级缓存第一级)。

一句话总结 ​

Spring 单例是"每个容器一个实例",不是"每个 JVM 一个实例";同一个 JVM 里多个容器/多个 classloader 可以有多个实例,集群下更是每台机器各一份——所以别把容器单例当全局单例用。

基于 VitePress 重建