Spring Bean 的六种作用域详解

在 Spring 容器中,Bean 的作用域决定了 Bean 实例的创建规则、生命周期和使用范围,它会直接影响程序的性能、线程安全和内存占用。Spring 一共定义了 6 种作用域,下面我们逐一拆解。


一、singleton(单例,默认作用域)

核心效果

  • 整个 Spring IoC 容器中,只会创建 1 个该 Bean 的实例,所有依赖该 Bean 的对象,拿到的都是同一个实例。
  • Bean 实例的生命周期与容器绑定:容器启动时初始化,容器关闭时销毁。

适用场景

  • 无状态的 Bean,比如 Service、DAO、工具类、配置类等。
  • 这类 Bean 不会保存线程 / 用户的私有状态,所有请求共用同一个实例,性能开销最低。

代码示例

复制代码
// 方式1:注解方式(默认就是 singleton,可以省略)
@Component
@Scope("singleton")
public class UserService {
}

// 方式2:XML 配置
<bean id="userService" class="com.example.service.UserService" scope="singleton"/>

二、prototype(原型 / 多例)

核心效果

  • 每次从容器中获取该 Bean 时,都会创建一个全新的实例
  • Spring 容器不负责 prototype Bean 的完整生命周期:容器只会初始化、装配 Bean,不会在关闭时销毁它,需要使用者自己管理销毁。

适用场景

  • 有状态的 Bean,比如需要保存用户私有数据的对象、多线程环境下的非线程安全对象。
  • 不适合频繁创建的大对象(会增加 GC 压力),适合轻量级、需要独立状态的 Bean。

代码示例

复制代码
@Component
@Scope("prototype")
public class OrderInfo {
    // 每个请求/线程都会拿到独立的 OrderInfo 实例
}

三、request(请求域)

核心效果

  • 每次 HTTP 请求,都会创建一个新的 Bean 实例,同一个请求内的所有对象拿到的都是同一个实例。
  • 仅在 Spring Web 环境中有效,实例的生命周期与 HTTP 请求绑定:请求结束,Bean 就会被销毁。

适用场景

  • 存储 HTTP 请求相关的临时数据,比如请求上下文、用户单次请求的参数对象。
  • 比如 Spring MVC 中,用于封装请求信息的对象。

代码示例

复制代码
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestInfo {
    // 每个 HTTP 请求都会创建独立实例
}

注意:需要配合 proxyMode 使用,否则会出现依赖注入异常。


四、session(会话域)

核心效果

  • 每个用户 HTTP 会话(Session),都会创建一个新的 Bean 实例,同一个 Session 内的所有请求,拿到的都是同一个实例。
  • 仅在 Spring Web 环境中有效,实例的生命周期与用户 Session 绑定:Session 超时 / 销毁,Bean 就会被销毁。

适用场景

  • 存储用户会话级别的状态数据,比如用户登录信息、购物车信息、用户偏好设置等。

代码示例

复制代码
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserSession {
    // 每个用户会话都会创建独立实例
}

五、application(应用域)

核心效果

  • 整个 Web 应用中,只会创建 1 个 Bean 实例,和 singleton 类似,但作用域是 ServletContext 级别的,比 singleton 范围更广(跨多个 Spring 容器也共享)。
  • 实例的生命周期与 Web 应用绑定:应用启动时初始化,应用关闭时销毁。

适用场景

  • 存储整个应用级别的全局数据,比如应用配置、全局统计信息、公共缓存对象。

代码示例

复制代码
@Component
@Scope(value = "application", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class AppConfig {
    // 整个 Web 应用中唯一实例
}

六、websocket(WebSocket 域)

核心效果

  • 每个 WebSocket 会话,都会创建一个新的 Bean 实例,同一个 WebSocket 会话内的消息处理,都会使用同一个实例。
  • 仅在 Spring WebSocket 环境中有效,实例的生命周期与 WebSocket 会话绑定:会话关闭,Bean 就会被销毁。

适用场景

  • WebSocket 长连接场景下,需要保存会话状态的对象,比如聊天室的用户连接信息、会话级别的消息处理器。

代码示例

复制代码
@Component
@Scope(value = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class WebSocketSessionInfo {
    // 每个 WebSocket 会话创建独立实例
}

作用域对比总结表

作用域 实例创建规则 适用环境 典型场景
singleton 容器中仅 1 个实例 所有环境 Service、DAO、工具类
prototype 每次获取都新建实例 所有环境 有状态的对象、非线程安全对象
request 每个 HTTP 请求 1 个实例 Web 环境 请求上下文、请求临时数据
session 每个用户 Session 1 个实例 Web 环境 用户登录信息、购物车
application 整个 Web 应用 1 个实例 Web 环境 全局配置、应用级缓存
websocket 每个 WebSocket 会话 1 个实例 WebSocket 环境 长连接会话状态

六、简易版总结

一、先搞懂:Bean 作用域到底是什么?

简单说,就是 Spring 给你创建的对象,是「共用一个」还是「每次都给新的」,以及能用多久。比如:

  • 有的对象整个程序里大家都用同一个(比如工具类)
  • 有的对象每次用都给你新的(比如购物车,每个用户都要有自己的)

二、6 种作用域,一个一个讲

1. singleton(单例,默认)

大白话:整个程序里,大家共用同一个对象

  • 效果:程序启动时,Spring 就创建好这一个对象,之后所有人调用,拿到的都是这同一个。
  • 例子:就像学校的「公共饮水机」,全校同学都用这一台,谁来接水都用它。
  • 什么时候用?没有自己的 "私有数据",大家用着都一样的对象,比如 Service 业务类、DAO 数据库操作类、工具类。

2. prototype(多例 / 原型)

大白话:每次调用,都给你一个全新的对象

  • 效果:你每次从 Spring 里拿这个对象,它都会给你新造一个,和之前的都不一样。
  • 例子:就像「一次性纸杯」,每个人喝水都给你一个新的,用完就扔,不会和别人共用。
  • 什么时候用?每个用户 / 每个操作都要有自己独立状态的对象,比如订单信息、购物车对象,不能和别人共用。

下面 4 种,只有做网页项目(Web 程序)才会用到,按「用多久」来分:

3. request(请求域)

大白话:一次网页请求,用一个对象

  • 效果:用户点一下网页(发一次请求),Spring 给你造一个对象,这次请求里全程都用它;等用户刷新页面 / 点下一个链接,这次请求结束,这个对象就扔了,下次再给新的。
  • 例子:就像「一次性纸巾」,你擦一次手就扔了,下次再拿新的。
  • 什么时候用?只在这一次网页请求里用的数据,比如用户这次搜索的关键词、临时请求参数。

4. session(会话域)

大白话:一个用户的一次登录会话,用一个对象

  • 效果:用户登录网站后,从登录到退出 / 超时这段时间,全程用同一个对象;用户退出登录,这个对象就销毁了。
  • 例子:就像「你自己的水杯」,你在学校一天都用这一个,放学回家就收起来,第二天再用新的(或者说,每个学生都有自己的水杯,不会共用)。
  • 什么时候用?要跟着用户登录状态走的数据,比如用户登录信息、购物车内容,用户不退出,这些数据就一直存在。

5. application(应用域)

大白话:整个网站,所有人共用同一个对象

  • 效果:网站启动时造好,整个网站运行期间,所有用户、所有请求都用这同一个对象,和 singleton 很像,但范围是整个网站。
  • 例子:就像「学校的公告栏」,全校所有同学都看这一个,公告内容更新了所有人都能看到。
  • 什么时候用?整个网站都要用的全局数据,比如网站的配置信息、全站访问量统计。

6. websocket(WebSocket 域)

大白话:一次 WebSocket 连接,用一个对象

  • 效果:用户和网站建立 WebSocket 长连接(比如聊天室、在线客服),连接期间全程用同一个对象;连接断开,对象就销毁了。
  • 例子:就像「你打电话时的专属通话通道」,你和客服通话期间,这个通道只属于你,挂电话就关了。
  • 什么时候用?WebSocket 长连接场景,比如聊天室里的用户连接信息、实时聊天的会话数据。

三、给你划重点(作业要写的核心点)

表格

作用域 一句话总结 关键特点
singleton 全程序共用 1 个 默认、无状态对象用它
prototype 每次调用给新的 有状态对象用它
request 一次请求用 1 个 网页单次请求用
session 一个用户会话用 1 个 登录用户的会话数据
application 全网站共用 1 个 全局配置 / 统计数据
websocket 一次 WebSocket 连接用 1 个 长连接场景用

四、举个你能懂的对比

  • 学校里:
    • singleton = 全校共用的操场
    • prototype = 每个同学自己的笔记本
    • request = 一次性考试草稿纸
    • session = 你自己的储物柜(从开学用到放假)
    • application = 学校的校史馆,所有人都看这一个
    • websocket = 一次视频通话的专用线路

关键补充说明

  1. 线程安全问题
    • singleton 单例 Bean 是线程共享的,必须保证线程安全(不能保存可变的成员变量状态)。
    • prototype/request/session 等多实例 Bean,每个线程 / 请求 / 会话使用独立实例,天生线程安全。
  2. 性能与内存
    • singleton 实例全局复用,性能最高、内存占用最低。
    • 多实例作用域会频繁创建销毁对象,会增加内存和 GC 压力,仅在有状态场景下使用。
  3. 代理模式
    • request/session/application 等 Web 相关作用域,需要配合 @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 使用,否则在注入 singleton Bean 时会出现依赖注入异常。
相关推荐
仙草不加料2 小时前
互联网大厂Java面试故事实录:三轮场景化技术提问与详细答案解析
java·spring boot·微服务·面试·aigc·电商·内容社区
程序员老邢2 小时前
【技术底稿 19】Redis7 集群密码配置 + 权限锁死 + 磁盘占满连锁故障真实排查全记录
java·服务器·经验分享·redis·程序人生·微服务
落魄江湖行2 小时前
基础篇一 Java 有了 int 为什么还要 Integer?它们到底差在哪?
java·面试·八股文
Rick19933 小时前
Spring AI 如何进行权限控制
人工智能·python·spring
星辰_mya3 小时前
OSI 七层模型之“跨国诈骗集团”深度讲解
运维·服务器·后端·面试·架构师
IT_陈寒3 小时前
SpringBoot自动配置这破玩意儿又坑我一次
前端·人工智能·后端
LiLiYuan.3 小时前
【Java 6种线程状态】
java·开发语言
itzixiao4 小时前
L1-047 装睡 (5分)[java][python]
java·开发语言·python
码事漫谈4 小时前
Cursor+Graphify实属强强联合了
后端