Spring 中 Bean 的作用域有哪些?
2026年02月01日
一则或许对你有用的小广告
欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于
Spring AI + Spring Boot3.x + JDK 21..., 点击查看; - 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot3.x + JDK 17..., 点击查看项目介绍; 演示链接: http://116.62.199.48:7070/; - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/
面试考察点
面试官提出这个问题,通常想考察以下几个层面的理解:
- 对 Spring IoC 容器基础概念的掌握程度: 是否理解 Bean 是 Spring 管理的核心对象,以及 “作用域” 决定了 Bean 的生命周期和可见范围。
- 对不同应用场景下 Bean 管理的认知: 不仅仅是记住有哪些作用域,更是想知道你是否理解在 单线程、多线程、Web 环境 等不同场景下,应该如何选择合适的作用域来保证程序的正确性、性能和数据隔离性。
- 对线程安全问题的敏感度: 例如,你是否清楚
singleton作用域的 Bean 在并发访问时可能存在的线程风险,以及如何规避。 - 对 Spring 容器工作机制的理解深度: 例如,
prototype作用域 Bean 的生命周期管理责任由谁承担,request作用域在 Web 环境中的具体创建和销毁时机等。
核心答案
Spring Framework 为 Bean 定义了多种作用域,你可以通过 @Scope 注解或 XML 配置来指定。在标准 Spring 容器中,主要有以下五种核心作用域:
singleton(单例,默认作用域): 在整个 Spring IoC 容器中,只存在一个该 Bean 的实例。所有对该 Bean 的依赖注入和查找,返回的都是同一个对象。prototype(原型): 每次通过容器获取该 Bean 时,都会创建一个新的实例。request: 在 Web 应用中,为每一个 HTTP 请求创建一个新的 Bean 实例。该实例仅在当前 HTTPrequest生命周期内有效。session: 在 Web 应用中,为每一个 HTTPSession创建一个新的 Bean 实例。该实例在整个用户会话期间有效。application: 在 Web 应用中,为整个ServletContext创建一个 Bean 实例。这可以理解为ServletContext级别的单例,其生命周期与ServletContext绑定。
注意:
request,session,application作用域仅在具有 Web 感知的 Spring 容器(如WebApplicationContext)中才有效,在纯 Spring 控制台应用中会报错。
深度解析
原理与机制
作用域的本质是 Spring 容器管理 Bean 实例创建和存储的策略。
- singleton: Bean 定义被加载后,容器就创建唯一实例并缓存到单例 Bean 注册表中。后续所有请求都返回该缓存引用。其生命周期与容器几乎相同。
- prototype: 容器在每次请求(如
getBean()或依赖注入)时,都执行new关键字创建新对象并返回。之后,容器便不再管理该实例的生命周期(即不负责其销毁)。对象的销毁需要由客户端代码或 GC 负责。 - request/session/application: 在 Web 环境中,Spring 通过
RequestContextListener或DispatcherServlet等机制,将 HTTP 请求/会话/应用上下文与当前线程绑定。容器从特定的作用域(如HttpServletRequest属性)中获取或创建 Bean。生命周期由对应的 Web 容器管理。
代码示例
// 1. 使用 @Scope 注解配置(最常用)
@Component
@Scope("singleton") // 或 @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class SingletonService {
// ...
}
@Component
@Scope("prototype") // 或 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeService {
// ...
}
@Controller
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserPreferenceController {
// 注意:当将 request/session 作用域的 Bean 注入到更长生命周期的 singleton Bean 时,
// 需要使用代理(如 TARGET_CLASS)。这里 proxyMode 是关键。
@Autowired
private UserPreferences userPreferences; // 假设是 @Scope(“request”) 的 Bean
}
// 2. XML 配置方式(了解即可)
<bean id="accountService" class="com.example.AccountServiceImpl" scope="prototype"/>
最佳实践与注意事项
- 默认使用 singleton: 无状态的服务层(Service)、数据访问层(DAO)、工具类 Bean 应优先使用
singleton,以减少对象创建开销,这也是 Spring 的默认选择。 - 有状态 Bean 需谨慎: 如果一个 Bean 含有可变的成员变量(状态),并且会被多线程访问,将其设为
singleton极有可能导致线程安全问题。此时,应考虑使用prototype、request或将状态变量(如SimpleDateFormat)改为 ThreadLocal 存储。 - Web 作用域的使用场景:
request: 存储当前请求的用户身份信息、表单数据等。session: 存储用户登录信息、购物车内容等。application: 存储应用级别的缓存、全局配置等。
- 原型模式的代价: 过度使用
prototype会增加 GC 压力,因为会产生大量短生命周期对象。仅在确实需要每次新实例时才使用。
常见误区
- 误以为
prototype能解决所有singleton的并发问题: 如果prototypeBean 本身被设计为有状态的,且其引用被多个线程共享,它依然存在线程安全问题。prototype解决的是“实例隔离”,而非 “线程安全”。 - 混淆容器生命周期与对象生命周期: 认为容器关闭时会自动销毁所有
prototypeBean。实际上,Spring 只负责创建,不负责销毁其prototypeBean。 - 在非 Web 环境使用 Web 作用域: 这会导致
BeanCreationException。 - 忽略代理模式(proxyMode): 当将短生命周期作用域的 Bean(如
request)注入到长生命周期的 Bean(如singletonController)时,必须在@Scope中设置proxyMode(如ScopedProxyMode.TARGET_CLASS)。因为注入发生在singletonBean 初始化时,此时request可能不存在。设置代理后,Spring 会注入一个代理对象,每次方法调用时代理会去当前作用域获取真实 Bean。
总结
Spring Bean 的五大作用域(singleton, prototype, request, session, application)是 IoC 容器管理对象粒度和生命周期的基础策略,核心选择依据是业务场景对实例数量、生命周期和数据隔离性的需求,正确的选择直接关系到应用的正确性、性能和资源管理。