在 Spring 框架中,Bean 的作用域(Scope)定义了 Bean 实例的生命周期和可见范围。Spring 提供了多种内置作用域,并支持自定义作用域。
1. Spring 内置的 Bean 作用域
1.1. singleton(单例) ✅ 默认作用域
- 描述:在整个 Spring IoC 容器中,该 Bean 只有一个实例。
- 生命周期:容器启动时创建(默认懒加载除外),容器关闭时销毁。
- 线程安全:不保证线程安全,需开发者自行处理(通常无状态组件无需担心)。
- 适用场景:无状态的服务类,如 Service、DAO、工具类等。
java
@Component
@Scope("singleton") // 可省略,默认就是 singleton
public class OrderService {
public void processOrder() { /* ... */ }
}
所有通过
@Component、@Service等注解声明的 Bean 默认都是 singleton。
1.2. prototype(原型)
- 描述:每次从容器请求该 Bean 时,都会创建一个新实例。
- 生命周期 :容器只负责创建,不管理销毁(调用者需自行清理资源)。
- 适用场景:有状态的对象,如购物车、临时计算上下文等。
java
@Component
@Scope("prototype")
public class ShoppingCart {
private List<Item> items = new ArrayList<>();
public void addItem(Item item) {
items.add(item);
}
}
注意:如果将
prototypeBean 注入到singletonBean 中,注入的仍是同一个实例(因为 singleton 初始化时就完成注入)。此时应使用ObjectProvider<ShoppingCart>或方法注入来获取新实例。
1.3. request(Web 作用域)
- 描述:每个 HTTP 请求创建一个新实例,请求结束时销毁。
- 适用范围 :仅在 Web 应用中可用(需
WebApplicationContext)。 - 适用场景:保存单次请求的数据,如请求参数封装对象。
java
@Component
@Scope("request")
public class RequestContext {
private String userId;
// getters/setters
}
在 Controller 中注入
RequestContext,每个 HTTP 请求都会获得独立实例。
1.4. session(Web 作用域)
- 描述:每个 HTTP 会话(HttpSession)创建一个 Bean 实例,会话结束时销毁。
- 适用范围:Web 应用。
- 适用场景:用户登录信息、会话级缓存等。
java
@Component
@Scope("session")
public class UserSession {
private String username;
private LocalDateTime loginTime;
}
同一会话中的多次请求共享同一个
UserSession实例。
1.5. application(Web 作用域)
- 描述:在整个 ServletContext 生命周期内只有一个实例(相当于 Web 应用级别的单例)。
- 与 singleton 区别 :
singleton是 Spring 容器级别;application是 ServletContext 级别(在 Web 环境中,通常两者行为一致,但语义不同)。
- 适用场景:全局配置、应用级缓存。
java
@Component
@Scope("application")
public class AppConfig {
private String appName = "MyApp";
}
实际开发中较少显式使用,多数情况用
singleton即可。
1.6. websocket(Web 作用域)
- 描述:每个 WebSocket 会话创建一个 Bean 实例。
- 适用范围:基于 WebSocket 的应用。
- 适用场景:WebSocket 连接相关的状态管理。
java
@Component
@Scope("websocket")
public class WebSocketHandler {
private String connectionId;
}
需配合 Spring WebSocket 模块使用。
2. 作用域对比表
| 作用域 | 范围 | 是否 Web 专用 | 实例数量 | 容器管理销毁 |
|---|---|---|---|---|
singleton |
整个 Spring 容器 | 否 | 1 个 | ✅ 是 |
prototype |
每次请求 | 否 | N 个(每次获取都新建) | ❌ 否 |
request |
单个 HTTP 请求 | ✅ 是 | 1 个/请求 | ✅ 是 |
session |
单个 HTTP 会话 | ✅ 是 | 1 个/会话 | ✅ 是 |
application |
整个 Web 应用(ServletContext) | ✅ 是 | 1 个/Web 应用 | ✅ 是 |
websocket |
单个 WebSocket 会话 | ✅ 是 | 1 个/WebSocket 连接 | ✅ 是 |
Spring 的作用域机制灵活支持从全局单例到请求/会话级实例的各种场景。默认使用 singleton 是出于性能和无状态设计的最佳实践,而 Web 相关作用域则为 Web 应用提供了细粒度的状态管理能力。
3. 注意事项
1. 非 Web 环境下使用 Web 作用域会报错 : 如在普通 Java SE 应用中使用 @Scope("request"),会抛出 IllegalStateException。
2. 代理模式解决作用域注入问题: 当短作用域 Bean(如 request)注入到长作用域 Bean(如 singleton)时,需使用代理:
java
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext { /* ... */ }
// 这样 Spring 会注入一个代理对象,每次调用方法时动态获取当前请求的实例。
3. 自定义作用域 : Spring 支持通过 ConfigurableBeanFactory.registerScope() 注册自定义作用域(如"租户"作用域等)。