后宫心计
111.02M · 2026-03-07
文章内容收录到个人网站,方便阅读:hardyfish.top/
Spring Boot 里的单例不是 GoF 意义上的全 JVM 唯一对象 + 私有构造器。
而是由 Spring 容器管理的每个 ApplicationContext 仅一个实例。
其本质是 IOC 容器用缓存表保存已创建的 Bean,并在 getBean() 时复用。
默认就是单例
@Component / @Service / @Repository / @Controller,或 @Bean,默认 scope=singleton。ApplicationContext(测试、子上下文)各有一份。@Component // 等价于 @Scope("singleton")
public class UserService {}
容器如何保证只有一个
核心在 DefaultSingletonBeanFactory/DefaultSingletonBeanRegistry 的三级缓存与同步控制:
singletonObjects:已完全创建好的单例。earlySingletonObjects:为解决循环依赖而“提前曝光”的半成品对象。singletonFactories:可生成早期引用的工厂(通常是 AOP 代理工厂)。创建流程(简化):
getBean(name) → 若在 singletonObjects 里,直接返回。createBean() 完成实例化、依赖注入、BeanPostProcessor 等。singletonObjects,清理二/三级缓存,返回单例。何时创建
preInstantiateSingletons())。@Lazy 或全局开启 spring.main.lazy-initialization=true 时,延迟到首次 getBean() 才创建,但仍是单例。线程安全与可见性
singletonObjects 前已完成依赖注入与后置处理,具备安全发布语义。@Bean 与 @Configuration 的细节
@Configuration 类会被 CGLIB 增强,其 @Bean 方法默认 proxyBeanMethods=true。
@Bean 方法时,调用会被拦截并从容器取同一个单例,不是 new 新对象。proxyBeanMethods=false(Spring Boot 为提速常用),同类内直接方法调用将变成普通方法调用,可能得到新对象。getBean() 拿的,仍是单例。经验:配置类间若互调 @Bean 方法,保持默认 proxyBeanMethods=true 或避免直接互调。
常见误区
ApplicationContext 就有两份。最小验证
@SpringBootTest
class DemoTest {
@Autowired UserService a;
@Autowired ApplicationContext ctx;
@Test void sameInstance() {
UserService b = ctx.getBean(UserService.class);
assertSame(a, b); // 同一容器内是同一个实例
}
}
小结