Spring概念:Bean作用域
在 Spring 框架里,Bean 作用域明确了 Spring 容器如何创建和管理 Bean 实例。不同的作用域适用于不同的应用场景,下面将详细介绍 Spring 中常见的 Bean 作用域。
1. singleton(单例)
解释
这是 Spring 默认的 Bean 作用域。当一个 Bean 被定义为单例作用域时,Spring 容器在整个应用的生命周期里只会创建该 Bean 的一个实例,并且所有对该 Bean 的请求都会返回这个唯一的实例。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
class MyBean {
public MyBean() {
System.out.println("MyBean 实例被创建");
}
}
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyBean bean1 = context.getBean(MyBean.class);
MyBean bean2 = context.getBean(MyBean.class);
System.out.println(bean1 == bean2); // 输出 true
context.close();
}
}
适用场景
适用于那些无状态或者状态可以共享的 Bean,像服务层组件、数据访问对象(DAO)等。由于单例作用域只创建一个实例,所以能减少内存开销。
可能存在的问题及解决方案
- 线程安全问题:若单例 Bean 是有状态的,多个线程同时访问该 Bean 时可能会出现线程安全问题。解决方案是保证 Bean 无状态,或者使用线程安全的类和同步机制。
2. prototype(原型)
解释
当一个 Bean 被定义为原型作用域时,每次从 Spring 容器请求该 Bean 时,容器都会创建一个新的实例。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class AppConfig {
@Bean
@Scope("prototype")
public MyBean myBean() {
return new MyBean();
}
}
class MyBean {
public MyBean() {
System.out.println("MyBean 实例被创建");
}
}
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyBean bean1 = context.getBean(MyBean.class);
MyBean bean2 = context.getBean(MyBean.class);
System.out.println(bean1 == bean2); // 输出 false
context.close();
}
}
适用场景
适用于有状态且状态不能共享的 Bean,比如需要为每个请求创建独立实例的场景。
可能存在的问题及解决方案
- 内存开销问题:由于每次请求都会创建新实例,若频繁请求可能会导致内存开销增大。解决方案是合理控制原型 Bean 的使用,避免不必要的创建。
3. request(请求)
解释
此作用域仅适用于 Web 应用的 Spring 上下文。在一个 HTTP 请求的生命周期内,容器会为每个请求创建一个 Bean 实例,不同的请求会有不同的实例。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;
@Configuration
public class AppConfig {
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public MyBean myBean() {
return new MyBean();
}
}
class MyBean {
public MyBean() {
System.out.println("MyBean 实例被创建");
}
}
适用场景
适用于需要在一个请求内保持状态的 Bean,例如处理用户请求时存储请求相关信息的 Bean。
可能存在的问题及解决方案
- 代理模式问题:在使用
request作用域时,通常需要使用代理模式(如ScopedProxyMode.TARGET_CLASS),否则可能会出现注入失败的问题。
4. session(会话)
解释
该作用域也仅适用于 Web 应用的 Spring 上下文。在一个用户会话的生命周期内,容器会为每个会话创建一个 Bean 实例,不同的会话会有不同的实例。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;
@Configuration
public class AppConfig {
@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public MyBean myBean() {
return new MyBean();
}
}
class MyBean {
public MyBean() {
System.out.println("MyBean 实例被创建");
}
}
适用场景
适用于需要在用户会话期间保持状态的 Bean,例如存储用户登录信息的 Bean。
可能存在的问题及解决方案
- 会话管理问题:如果会话过期或者被销毁,对应的 Bean 实例也会失效。需要确保在会话管理方面进行合理配置。
5. application(应用)
解释
同样适用于 Web 应用的 Spring 上下文。在整个 Web 应用的生命周期内,容器只会创建一个 Bean 实例,类似于单例作用域,但它是基于 ServletContext 的。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;
@Configuration
public class AppConfig {
@Bean
@Scope(value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public MyBean myBean() {
return new MyBean();
}
}
class MyBean {
public MyBean() {
System.out.println("MyBean 实例被创建");
}
}
适用场景
适用于需要在整个 Web 应用中共享状态的 Bean,比如全局计数器等。
可能存在的问题及解决方案
- 资源竞争问题:多个线程同时访问应用作用域的 Bean 时可能会出现资源竞争问题。可以使用同步机制来解决。
6. websocket(WebSocket)
解释
这是 Spring 4.2 引入的作用域,适用于 WebSocket 应用。在一个 WebSocket 会话的生命周期内,容器会为每个 WebSocket 会话创建一个 Bean 实例。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
@Configuration
@EnableWebSocket
public class AppConfig {
@Bean
@Scope(value = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public MyBean myBean() {
return new MyBean();
}
}
class MyBean {
public MyBean() {
System.out.println("MyBean 实例被创建");
}
}
适用场景
适用于处理 WebSocket 会话相关的 Bean,比如存储 WebSocket 会话状态的 Bean。
可能存在的问题及解决方案
- 会话管理复杂:WebSocket 会话的管理相对复杂,需要确保在会话关闭时正确处理 Bean 实例。可以在 WebSocket 处理器中添加相应的关闭逻辑。
简单总结:
Spring Bean作用域(大白话版)
简单来说,Bean作用域决定了Spring容器怎么“生产”和“管理”对象,就像工厂生产产品的不同模式。
1. 单例(默认模式)
- 解释:整个程序运行期间只生产1个对象,所有人都用这一个。
- 例子:公司的打印机,全公司共用一台。
- 问题:如果这个对象要记录不同人的状态(比如记录打印次数),就会打架。
- 解决:别让它保存个人状态(比如用局部变量代替成员变量)。
2. 原型(每次要都新建)
- 解释:每次需要就新建一个对象,用完不管。
- 例子:奶茶店的一次性杯子,每个顾客拿一个新的。
- 问题:如果频繁创建大量对象(比如每秒1000次请求),内存可能不够用。
- 解决:尽量复用对象,或者用连接池等技术优化。
3. 请求(Web项目专属)
- 解释:每个HTTP请求来的时候创建一个对象,请求结束就销毁。
- 例子:银行柜台窗口,每个顾客办理业务时分配一个临时柜员。
- 注意:如果其他组件(比如Service层)直接引用这个对象,可能会拿不到最新的,需要特殊配置(类似“代购”)。
4. 会话(Web项目专属)
- 解释:用户登录后创建一个对象,用户关闭浏览器前一直用这个。
- 例子:用户的购物车,整个购物过程都用同一个。
- 问题:如果用户一直不关闭浏览器,可能导致内存泄漏。
- 解决:设置会话超时时间,或者手动清理。
5. 应用(Web项目全局)
- 解释:整个网站运行期间只创建1个对象,所有用户共享。
- 例子:网站的访问计数器,所有人看到的数字都是同一个。
- 问题:多个用户同时修改会打架(比如同时增加计数器)。
- 解决:用线程安全的类(如AtomicInteger)或者加锁。
6. WebSocket(实时通信专属)
- 解释:每个WebSocket连接创建一个对象,连接断开就销毁。
- 例子:直播间的弹幕处理器,每个观众的连接都有独立的处理逻辑。
- 问题:大量用户同时连接时,内存压力大。
- 解决:合理设计业务逻辑,避免长时间占用资源。
总结选择原则:
- 优先用单例(最省内存)
- 有状态且需要隔离用原型
- Web项目中按场景选请求/会话/应用
- 实时通信才用WebSocket作用域

浙公网安备 33010602011771号