内容纲要
						
						是的,SpringBoot 中的 @Controller(以及 @RestController)默认是 单例(Singleton) 的。
原因在于:
- 
Spring Bean 默认作用域
- Spring 容器管理的所有 Bean 默认作用域是 
singleton。 @Controller只是一个特殊的 Bean 注解,本质上和@Component一样,默认作用域同样是单例。
 - Spring 容器管理的所有 Bean 默认作用域是 
 - 
请求处理的线程安全问题
- 每次请求并不会创建新的 Controller 实例,而是由多个线程共享同一个 Controller 对象。
 - 这意味着 不要在 Controller 中定义有状态的成员变量(例如:可变的 List、Map、计数器等),否则在并发访问时可能会发生线程安全问题。
 - 正确做法是:把状态交给方法参数、本地变量,或者交给 Service / Repository 层处理。
 
 - 
如何修改作用域
如果确实有需求让 Controller 不是单例,可以用@Scope修改,比如:@Controller @Scope("prototype") // 每次请求都会创建一个新的实例 public class MyController { }但这种情况比较少见,因为 Spring MVC 的设计理念就是 Controller 是无状态的,单例即可。
 
👉 总结:
- 默认单例,所有请求共享同一个 Controller 实例。
 - 注意线程安全,避免在 Controller 中定义有状态成员变量。
 - 特殊需求可以用 
@Scope("prototype")改成多例。 
🌐 SpringMVC 请求到 Controller 的流程(简化版)
这张图会帮你更直观理解为什么是单例
sequenceDiagram
    participant Browser as 浏览器/客户端
    participant Dispatcher as DispatcherServlet
    participant HandlerMapping as HandlerMapping
    participant Controller as @Controller (单例Bean)
    participant Service as Service层
    participant View as ViewResolver
    Browser->>Dispatcher: 发送HTTP请求
    Dispatcher->>HandlerMapping: 查找对应的Controller方法
    HandlerMapping->>Dispatcher: 返回Handler(Controller+Method)
    Dispatcher->>Controller: 调用Controller方法 (同一个单例对象)
    Controller->>Service: 调用业务逻辑
    Service-->>Controller: 返回结果数据
    Controller-->>Dispatcher: 返回ModelAndView/ResponseBody
    Dispatcher->>View: 渲染视图
    View-->>Browser: 返回HTTP响应
↑↑↑Spring MVC 请求到 Controller 的生命周期示意图
🔑 关键点说明
- 
Controller 是单例
- DispatcherServlet 每次请求过来时,会根据 HandlerMapping 找到已经注册好的 Controller Bean(单例),然后调用它的方法。
 - 不是每个请求都 
new一个 Controller。 
 - 
线程安全风险
- 因为同一个 Controller 实例会被多个线程调用,如果你在类里定义了共享的可变成员变量,可能会造成数据混乱。
 - 正确做法:方法内使用局部变量,或者把状态托管到 
Service、DB、Redis等。 
 - 
生命周期对比
- 单例 Controller:应用启动时就实例化,整个应用共享。
 - 原型 Controller (
@Scope("prototype")):每次注入或请求都会新建实例,但几乎没人这样用,会有性能和管理成本。 
 
Controller 单例 vs 多例 对比表
📊 SpringBoot Controller 作用域对比
| 特性 | 默认单例(@Controller) | 
多例(@Controller @Scope("prototype")) | 
|---|---|---|
| 生命周期 | Spring 容器启动时创建一个实例,整个应用中共享 | 每次注入 / 调用都会新建一个实例 | 
| 内存开销 | 较低(只保留一个实例) | 较高(频繁创建和销毁对象) | 
| 线程安全 | ⚠️ 有风险:多个线程共享同一个对象,若存在成员变量则可能冲突 | ✅ 安全:每个请求独立实例,不会共享成员变量 | 
| 常见使用场景 | 绝大多数情况(Controller 方法无状态) | 少见:需要在 Controller 内维护独立的会话级 / 请求级状态时 | 
| 性能 | 高效(单例复用) | 低效(频繁创建销毁) | 
| 推荐实践 | 保持 Controller 无状态,把状态交给方法参数、Service、数据库 | 尽量避免;如果需要请求隔离,更推荐 @Scope("request") | 
💡 结论
- 绝大部分业务场景下,单例足够且推荐。
 - 
如果确实要存储和隔离请求状态,可以考虑:
- 使用 
@Scope("request"),保证每个 HTTP 请求独立一个 Controller 实例。 - 使用 
ThreadLocal保存线程级变量(例如登录用户信息)。 - 将有状态的数据下放到 Service / Session / Redis,而不是放在 Controller 成员变量里。
 
 - 使用