中 基础
Spring-Bean作用域#
一句话答案#
Spring Bean 默认 Singleton(单例),还支持 Prototype(每次 new)、Request、Session、Application 五种作用域,Web 环境下后三种才可用。
核心要点
五种作用域#
| 作用域 | 含义 | 生命周期 |
|---|---|---|
| singleton | 容器中只有一个实例(默认) | 随容器创建和销毁 |
| prototype | 每次 getBean() 创建新实例 | 容器不管理销毁,由调用方负责 |
| request | 每个 HTTP 请求一个实例 | 请求结束时销毁 |
| session | 每个 HTTP Session 一个实例 | Session 过期时销毁 |
| application | 每个 ServletContext 一个实例 | 应用关闭时销毁 |
Singleton 与 Prototype 对比#
| 维度 | Singleton | Prototype |
|---|---|---|
| 实例数 | 1 个 | 每次请求新建 |
| 线程安全 | 需要自己保证(无状态设计) | 天然线程安全(各用各的) |
| 销毁回调 | 容器调用 @PreDestroy | 容器不负责,需手动管理 |
| 性能 | 好(复用) | 差(频繁创建) |
Singleton Bean 注入 Prototype Bean 的问题#
@Component // 默认 Singleton
public class SingletonBean {
@Autowired
private PrototypeBean proto; // 注入时只创建一次!后续用的都是同一个实例
}java问题: Singleton Bean 只初始化一次,注入的 Prototype Bean 也只创建一次,后续调用用的都是同一个实例。
解决方案:
- @Lookup 注解:让 Spring 每次调用时动态创建 Prototype
- ObjectFactory / Provider:
@Autowired ObjectFactory<PrototypeBean> factory;每次调factory.getObject() - ApplicationContext.getBean():手动从容器获取
实际使用场景#
- Singleton:无状态的 Service、DAO、Controller(99% 的 Bean)
- Prototype:有状态的对象(如每次请求需要独立的处理器实例)
- Request:存储当前请求的用户信息
- Session:购物车
面试回答(2分钟版)
Spring Bean有五种作用域。Singleton是默认的,整个容器只有一个实例,随容器启动创建和关闭销毁,99%的Service、DAO、Controller都用这种。Prototype是每次getBean都创建新实例,容器不负责销毁需要调用方自行管理。Request、Session和Application是Web环境专用的,分别对应每个HTTP请求、每个会话和整个ServletContext一个实例。面试最常考的坑是Singleton注入Prototype的问题:因为Singleton只初始化一次,注入的Prototype也只创建一次,后续用的都是同一个实例,违背了Prototype的本意。解决方案有三种:用@Lookup注解让Spring每次动态创建、用ObjectFactory或Provider每次调getObject获取新实例、或者直接通过ApplicationContext.getBean手动获取。另外Singleton Bean不是线程安全的,如果有状态字段需要用ThreadLocal或同步机制自行保证。
追问与易错
追问方向:
- “Singleton Bean 线程安全吗?”→ 不是,需要自己保证(无状态 / ThreadLocal)
- “Prototype Bean 的销毁谁管?”→ 容器不管,需自己调用或用 DestructionAwareBeanPostProcessor
易错点:
- ❌ “Singleton 就是线程安全的”——单例 ≠ 线程安全,有状态字段需同步
- ❌ “Prototype 注入到 Singleton 还是每次新建”——不是,只在注入时创建一次