面试必问:Spring Bean 的作用域类型有多少种?

原文来自于:zha-ge.cn/java/132

面试必问:Spring Bean 的作用域类型有多少种?

有一次去面试,面试官看着我笑:"那你说说,Spring Bean 有几种作用域?" 我当时想都没想就脱口而出:"不就 singleton 和 prototype 吗?" 他点点头,然后淡定地补了一句:"那 request、session、application 呢?" ......空气突然安静。

这题看似简单,其实是 Spring IoC 理解的分水岭题 。 Bean 作用域(Scope)决定了你的对象被创建几次、在哪个上下文中存活、谁来管理销毁。 没整明白这一层,分分钟在多线程或 Web 项目里翻车。


一、Bean 作用域是干嘛的?

一句话:作用域定义了 Bean 的生命周期范围。

Spring 容器里,每一个 Bean 都有一个"生存空间":

  • 什么时候被创建?
  • 谁能访问它?
  • 会不会被共享?
  • 用完了要不要销毁?

这些问题都由作用域(Scope)决定。 默认情况下,Spring Bean 是单例的(singleton),但远不止这一种。


二、Spring 提供的五种常见作用域

Spring 一共支持 5 种标准作用域(在 Web 应用中才有后 3 种),外加自定义作用域机制。

作用域 说明 适用场景
singleton 容器中只有一个 Bean 实例(默认) 绝大多数服务类、DAO 类
prototype 每次注入或获取都会创建新实例 有状态对象、临时任务类
request 每个 HTTP 请求创建一个 Bean Web 请求级别逻辑处理
session 每个用户会话创建一个 Bean 用户状态保持(购物车、登录信息)
application 整个 Web 应用共享一个 Bean 全局配置、缓存、统计数据

三、来点代码感受下每种 Scope 的差异

1️⃣ singleton ------ 全局唯一,容器启动即创建

java 复制代码
@Component
@Scope("singleton")
public class AppConfigService { }

特征:

  • 默认作用域。
  • Spring 容器启动时创建。
  • 所有注入点共用同一个实例。

适合: 无状态服务、DAO、工具类。


2️⃣ prototype ------ 每次拿一个新对象

java 复制代码
@Component
@Scope("prototype")
public class TaskProcessor { }

特征:

  • 每次调用 getBean() 或注入时都会新建实例。
  • Spring 不负责销毁(生命周期由调用者决定)。

适合: 临时任务、策略对象、线程隔离的组件。

⚠️ 注意: prototype Bean 注入到单例 Bean 中时,只会创建一次! 想每次都拿新对象,得配合 ObjectFactory@Lookup


3️⃣ request ------ 每个 HTTP 请求单独创建

java 复制代码
@Component
@Scope("request")
public class RequestScopedBean { }

特征:

  • 每个 HTTP 请求创建一个实例。
  • 请求结束后自动销毁。
  • 只能在 Web 环境使用(需有 WebApplicationContext)。

适合: 请求级的日志追踪、Request ID 管理、临时缓存。


4️⃣ session ------ 每个用户会话独立实例

java 复制代码
@Component
@Scope("session")
public class SessionScopedBean { }

特征:

  • 每个浏览器会话维护一个实例。
  • Session 失效时 Bean 一并销毁。

适合: 登录态、购物车、用户偏好设置等。


5️⃣ application ------ 全局级共享

java 复制代码
@Component
@Scope("application")
public class AppScopedBean { }

特征:

  • 对应 ServletContext 生命周期。
  • 所有请求、会话共享同一实例。

适合: 全局配置、全站缓存、统计信息、数据池。


四、进阶:自定义作用域(Custom Scope)

除了这五种内置的作用域,Spring 还允许你注册自定义 Scope。

比如你想让某个 Bean 在"线程"级别隔离,就可以自己定义一个 ThreadScope

java 复制代码
public class ThreadScope implements Scope {
    private final ThreadLocal<Map<String, Object>> threadBeans = ThreadLocal.withInitial(HashMap::new);

    @Override
    public Object get(String name, ObjectFactory<?> factory) {
        return threadBeans.get().computeIfAbsent(name, k -> factory.getObject());
    }

    @Override
    public void remove(String name) {
        threadBeans.get().remove(name);
    }
}

然后注册到容器:

java 复制代码
@Bean
public static CustomScopeConfigurer customScopeConfigurer() {
    CustomScopeConfigurer configurer = new CustomScopeConfigurer();
    configurer.addScope("thread", new ThreadScope());
    return configurer;
}

使用时只需:

java 复制代码
@Scope("thread")
@Component
public class ThreadLocalBean { }

🎯 这样每个线程都会维护自己的一份 Bean 实例------非常适合异步任务或线程隔离场景。


五、常见坑点与解决思路

坑点 现象 解决方案
prototype 注入单例后不变 Bean 每次都一样 改为 ObjectProvider<T>@Lookup
requestsession 在非 Web 环境使用 报错:Scope not active 确保使用 WebApplicationContext,或改为 singleton
自定义作用域 Bean 不销毁 内存泄漏 实现 Scope#registerDestructionCallback()
Web 环境作用域失效 多线程异步任务拿不到 RequestContextHolder 手动绑定上下文

六、面试官最爱问的三连

1. Spring 默认作用域是什么?singleton,容器启动时创建,全局唯一。

2. singletonprototype 有何区别?singleton 容器管理全生命周期;prototype 每次创建新对象,容器不负责销毁。

3. Web 环境下有哪三种作用域?requestsessionapplication

4. 可以自定义作用域吗? ✅ 可以,实现 org.springframework.beans.factory.config.Scope 接口并注册。


七、总结:作用域是 Bean 生命周期的"地理边界"

很多人理解 Bean 生命周期,却忽略了"它在哪活着"这件事。 Spring 的 Scope 就像给 Bean 发的"居住证",决定了它在哪被创建、由谁销毁、能被谁看到。

一句话记住:

"singleton 管一生,prototype 过一瞬,request 短相遇,session 随用户,application 全局共享。"

相关推荐
WHOVENLY13 分钟前
揭秘正则表达式的基础语法与应用
开发语言·javascript·正则表达式
李慕婉学姐21 分钟前
Springboot旅游景点管理系统2fj40iq6(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
蓝眸少年CY43 分钟前
(第八篇)spring cloud之zuul路由网关
后端·spring·spring cloud
long3161 小时前
弗洛伊德·沃肖算法 Floyd Warshall Algorithm
java·后端·算法·spring·springboot·图论
Loo国昌1 小时前
【LangChain1.0】第一篇:基础认知
后端·python·算法·语言模型·prompt
源代码•宸1 小时前
Golang原理剖析(channel面试与分析)
开发语言·经验分享·后端·面试·golang·select·channel
光算科技1 小时前
单页应用SEO可行性丨Angular项目的3个索引优化方案
前端·javascript·angular.js
小程同学>o<1 小时前
嵌入式之ARM体系与架构面试题(一)硬件基础篇
arm开发·笔记·学习·面试·架构
鹿角片ljp2 小时前
Java多线程编程:从基础到实战的完整指南
java·开发语言·后端
顾林海2 小时前
Android登录模块设计:别让“大门”变成“破篱笆”
android·经验分享·面试·架构·移动端