
spring 默认创建的单例 bean 是全局共享的,若在其中修改可变状态(如实例变量),将导致多线程间数据污染,破坏应用稳定性;正确做法是保持 bean 无状态,或使用 threadlocal、作用域(如 prototype)隔离上下文。
在 Spring 应用中,绝大多数 Bean(如 @Service、@Repository、@Component)默认以 singleton 作用域创建——即整个 Spring 容器中仅存在一个实例,被所有请求、线程和组件共享。这意味着:对该 Bean 实例成员变量(非 final、非线程安全类型)的任何修改,都会直接影响其他并发调用的行为。
例如,以下代码存在严重线程安全问题:
@Service
public class UnsafeContextService {
private String currentUser; // ❌ 危险:共享可变状态
public void setCurrentUser(String user) {
this.currentUser = user; // 多线程调用会互相覆盖
}
public String getCurrentUser() {
return currentUser;
}
}当两个 HTTP 请求(由不同线程处理)先后调用 setCurrentUser("A") 和 setCurrentUser("B") 时,currentUser 的值可能被任意覆盖,后续调用 getCurrentUser() 返回结果不可预测——这正是典型的竞态条件(Race Condition)。
✅ 正确实践包括:
保持 Bean 无状态(Stateless):只依赖方法参数和不可变对象,避免在单例 Bean 中存储请求/用户相关数据;
-
使用 ThreadLocal 隔离线程上下文(适用于需跨方法传递的临时状态):
@Service public class ThreadLocalContextService { private static final ThreadLocalcurrentUser = ThreadLocal.withInitial(() -> null); public void setCurrentUser(String user) { currentUser.set(user); } public String getCurrentUser() { return currentUser.get(); } public void clear() { currentUser.remove(); // ⚠️ 必须显式清理,防止内存泄漏(尤其在线程池场景) } } 选用合适的作用域:对需独立实例的场景,改用 @Scope("prototype")(每次注入新建实例),但需注意原型 Bean 不参与 AOP 代理生命周期管理;
利用 Web 层上下文:在 Spring MVC 中,优先通过 @RequestScope Bean、HttpServletRequest 或 SecurityContextHolder 获取当前请求/认证信息,而非在单例中“缓存”上下文。
⚠️ 重要提醒:
- Spring 的设计哲学是通过依赖注入解耦协作,而非让 Bean 承担状态管理职责;
- 一旦在单例 Bean 中引入可变状态,不仅破坏线程安全,更使单元测试难以隔离、系统行为难以预测;
- 若业务确需上下文感知能力,应明确选择 @RequestScope、@SessionScope 或结合 InheritableThreadLocal(慎用)等机制,而非绕过 Spring 约定直接修改单例字段。
归根结底:Spring Bean 不是状态容器,而是协作组件——它的价值在于复用逻辑,而非存储数据。










