
spring 默认创建的单例 bean 是全局共享的,若在其中修改可变状态(如实例变量),会导致多线程间数据污染;正确做法是保持 bean 无状态,或使用 threadlocal、作用域(如 prototype)隔离上下文。
在 Spring 应用中,绝大多数 Bean(如 @Service、@Repository、@Component)默认以 singleton 作用域创建——即整个 Spring 容器中仅存在一个实例,被所有请求、线程和调用共享。这意味着:如果你在某个 HTTP 请求处理过程中,直接修改了该 Bean 的可变实例变量(如 private String contextId = "old" → contextId = "new"),那么这个新值将立即对其他并发请求可见,极可能导致严重的线程安全问题与数据污染。
❗ 错误示例:在单例 Bean 中维护可变状态
@Service
public class OrderProcessor {
private String currentUser; // 危险!共享的可变状态
public void processOrder(Long orderId) {
// 假设从当前请求中提取用户信息并赋值
this.currentUser = SecurityContextHolder.getContext()
.getAuthentication().getName(); // ← 修改共享字段!
// 后续逻辑依赖 currentUser...
log.info("Processing order {} for user {}", orderId, currentUser);
}
}⚠️ 问题:多个请求并发调用 processOrder() 时,currentUser 可能被相互覆盖(如请求 A 设为 "alice",请求 B 紧接着设为 "bob"),导致请求 A 的后续日志或业务逻辑误用 "bob" —— 这是典型的竞态条件。
✅ 正确实践:避免在单例 Bean 中存储请求/线程相关状态
-
保持 Bean 无状态(推荐)
将上下文数据作为方法参数传递,而非存于 Bean 字段:@Service public class OrderProcessor { public void processOrder(String currentUser, Long orderId) { log.info("Processing order {} for user {}", orderId, currentUser); } } -
使用 ThreadLocal(谨慎使用)
若必须在线程内暂存状态(如日志 MDC、事务上下文),可用 ThreadLocal 隔离:@Service public class RequestContextHolder { private static final ThreadLocalCURRENT_USER = new ThreadLocal<>(); public static void setCurrentUser(String user) { CURRENT_USER.set(user); } public static String getCurrentUser() { return CURRENT_USER.get(); } public static void clear() { // 必须在请求结束时清理(如 Filter 或 Interceptor) CURRENT_USER.remove(); } } ⚠️ 注意:ThreadLocal 不自动清理,需配合 @PreDestroy、Filter 或 Spring 的 RequestContextHolder 使用,否则可能引发内存泄漏(尤其在 Tomcat 线程池复用场景)。
-
改用非单例作用域(按需)
对真正需要独立状态的组件,声明为 @Scope("prototype") 或 @Scope("request"):@Scope("request") // 每个 HTTP 请求创建新实例 @Component public class RequestScopedContext { private String requestId = UUID.randomUUID().toString(); // 此字段仅对该请求可见,线程安全 }
总结
- ✅ Spring 单例 Bean 的设计初衷是无状态的服务协调者,而非“上下文容器”;
- ❌ 直接修改其字段 = 破坏依赖注入契约 + 引入隐蔽的并发 Bug;
- ? 正确解法 = 参数化传入上下文、用 ThreadLocal 隔离线程状态(配清理机制)、或选用 request/prototype 作用域;
- ?️ 生产环境务必通过压力测试 + 代码审查,杜绝在 singleton Bean 中写入可变实例变量。










