Spring Bean 的作用域定义了 Bean 实例在 Spring IOC 容器中的创建方式和生命周期。以下是几种主要的作用域及其优缺点(前两个为常见作用域):
1. Singleton (单例)
优点:
性能高效:由于在整个应用上下文中只创建一个实例,减少了对象创建的开销。
状态共享:对于无状态的 Bean 很适用,所有请求共享同一实例,方便数据共享。
简单易用:作为默认作用域,开发者无需特别配置即可使用。
缺点:
线程安全问题:对于有状态的 Bean,多个线程同时访问可能会引发并发问题,需要开发者自行确保线程安全。
灵活性较低:所有请求都复用同一实例,不适合需要独立实例的场景。
2. Prototype (原型)
优点:
线程安全:每次请求都会创建一个新的实例,避免了多线程间的数据竞争问题。
灵活性高:适合需要独立实例或有状态 Bean 的场景。
缺点:
性能开销:频繁创建和销毁实例可能导致性能下降。
生命周期管理:需要开发者手动管理 Bean 的创建和销毁,增加了编程复杂度。
3. Request (请求)
优点:
线程安全:每个 HTTP 请求都会创建一个新的 Bean 实例,天然地隔离了请求之间的状态。
资源节约:请求结束后自动销毁,避免了不必要的资源占用。
缺点:
适用范围限制:仅适用于 Web 应用程序,且需要特定的 Servlet 容器支持。
生命周期短:仅在请求生命周期内有效,对于跨请求的数据管理不适用。
4. Session (会话)
优点:
持久化会话状态:每个用户会话创建一个实例,可以用来保存会话级别的数据。
线程安全:每个用户拥有独立的实例,避免了并发冲突。
缺点:
资源消耗:长时间的会话可能导致内存占用增加。
适用范围限制:同样局限于 Web 应用,并且依赖于会话管理机制。
5. Global Session (全局会话)
优点:
在 Portlet 环境中,为跨多个 Portlet 的会话提供共享数据。
缺点:
使用场景有限,仅在 Portlet 应用中使用。
管理复杂,需要理解 Portlet 规范中的会话概念。
关于线程并发安全:
作用域调整:
Singleton(默认):Spring 默认的 Bean 作用域是 Singleton,这意味着在整个应用上下文中只存在一个共享的 Bean 实例。对于 Singleton Bean,开发人员需要自行确保线程安全,因为多个线程可能同时访问同一个实例。可以通过减少可变状态、使用不可变对象或同步代码块等方式来保证线程安全。
Prototype:如果一个 Bean 需要在每次请求时创建新的实例,可以将其作用域设置为 Prototype,这样每个线程将获得自己的实例,从而自然地避免了并发问题。
Web 相关作用域:如 Request、Session 和 Application,这些作用域的 Bean 根据 HTTP 请求、会话或整个应用程序生命周期创建,它们天然具有某种程度的线程隔离。
ThreadLocal:在某些情况下,可以使用 ThreadLocal 来存储线程特有的数据,以此来避免多线程间的资源共享问题,但这要求开发者谨慎使用以避免内存泄漏。