spring boot的单例模式是如何实现的?

1 个回答

  • 火灵儿
    世间万物,唯有过往伤人最深。
    Spring Boot 里的“单例”不是 GoF 意义上的“全 JVM 唯一对象 + 私有构造器”,而是**由 Spring 容器管理的“每个 ApplicationContext 仅一个实例”**。其本质是 IoC 容器用缓存表保存已创建的 bean,并在 `getBean()` 时复用。 # 1) 默认就是单例 * 任何 `@Component` / `@Service` / `@Repository` / `@Controller`,或 `@Bean`,**默认 scope=singleton**。 * 单例是**容器级别**:多个 `ApplicationContext`(测试、子上下文)各有一份。 ```java @Component // 等价于 @Scope("singleton") public class UserService {} ``` # 2) 容器如何保证“只有一个” 核心在 `DefaultSingletonBeanFactory`/`DefaultSingletonBeanRegistry` 的**三级缓存**与同步控制: * **一级缓存** `singletonObjects`:已完全创建好的单例。 * **二级缓存** `earlySingletonObjects`:为解决循环依赖而“提前曝光”的半成品对象。 * **三级缓存** `singletonFactories`:可生成早期引用的工厂(通常是 AOP 代理工厂)。 创建流程(简化): 1. `getBean(name)` → 若在 `singletonObjects` 里,直接返回。 2. 不在 → 标记“正在创建”,调用 `createBean()` 完成实例化、依赖注入、`BeanPostProcessor` 等。 3. 初始化完成后放入 `singletonObjects`,清理二/三级缓存,返回单例。 4. 全过程对同名 bean 的创建有**同步锁**,避免并发重复创建;并通过“早期引用”解决 A→B→A 的**构造/注入级循环依赖**。 # 3) 何时创建 * 默认**容器刷新**时预创建(`preInstantiateSingletons()`)。 * 标注 `@Lazy` 或全局开启 `spring.main.lazy-initialization=true` 时,延迟到首次 `getBean()` 才创建,但**仍是单例**。 # 4) 线程安全与可见性 * 创建阶段受容器同步保护,发布到 `singletonObjects` 前已完成依赖注入与后置处理,具备**安全发布**语义。 * 但**单例是多线程共享**:业务字段若是可变状态,需自行保证线程安全(不可变、同步、并发集合等)。 # 5) `@Bean` 与 `@Configuration` 的细节 * `@Configuration` 类会被 **CGLIB 增强**,其 `@Bean` 方法默认 `proxyBeanMethods=true`。 * 在同一配置类内部互相调用 `@Bean` 方法时,调用会被拦截并**从容器取同一个单例**,不是 new 新对象。 * 若显式 `proxyBeanMethods=false`(Spring Boot 为提速常用),同类内**直接方法调用**将变成普通方法调用,可能得到**新对象**;只要是通过容器 `getBean()` 拿的,仍是单例。 → 经验:配置类间若互调 `@Bean` 方法,保持默认 `proxyBeanMethods=true` 或避免直接互调。 # 6) 常见误区 * “Spring 单例 = JVM 唯一” ❌:是**每个容器**一个。你起了两个 `ApplicationContext` 就有两份。 * 用 static/单例写法再交给 Spring 管理 ❌:多此一举且易与容器生命周期冲突。 * 循环依赖全能解 ❌:仅能解**单例构造器以外的注入型**循环依赖;构造器循环、原型 scope、带代理的某些场景仍会失败。 # 7) 最小验证 ```java @SpringBootTest class DemoTest { @Autowired UserService a; @Autowired ApplicationContext ctx; @Test void sameInstance() { UserService b = ctx.getBean(UserService.class); assertSame(a, b); // 同一容器内是同一个实例 } } ``` # 小结 Spring Boot 的“单例”= **默认 scope + BeanFactory 单例缓存 + 同步创建 +(必要时)早期曝光**。 把“唯一性”交给容器,比手写 GoF 单例更易测、更可控,也能与 AOP、生命周期、条件化装配等机制无缝协作。
    发布于 2025-08-19 05:11 •IP属地 海南
    举报
    评论
    收藏