You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中SimpleThreadScope同一线程为何生成两个对象实例?

问题分析:同一线程中ThreadScope实例通过@Autowired和getBean获取的对象看似不同

核心原因:代理对象与实际实例的区别

你看到的“两个不同实例”并不是真的在同一线程中创建了两个ThreadScopeObject实例,而是Spring为线程作用域Bean创建的代理对象和当前线程的实际实例的区别。

具体来说:

  • ThreadScopeService是Spring默认的单例Bean,当它初始化时,Spring不能直接注入一个线程作用域的实例(因为单例Bean的生命周期和线程无关),所以会自动为ThreadScopeObject生成一个代理对象。
  • 这个代理对象的作用是:每次你调用它的方法时,都会从当前线程的ThreadLocal中取出真正的线程作用域实例。
  • 而你通过context.getBean()直接获取的是当前线程中的实际实例,所以两者的toString()输出不同,但背后指向的实际实例是同一个。

从你的日志也能验证这一点:只有一条new object的初始化日志,说明实际上只创建了一个ThreadScopeObject实例,另一个“实例”只是代理对象的标识。


验证方法

你可以通过属性值来验证两者指向同一个实际实例:

// 在objectInstanceTest()中修改代码
@Async
public CompletableFuture<Map> objectInstanceTest() {
    ThreadScopeObject o = ApplicationContextHolder.getContext().getBean(ThreadScopeObject.class);
    // 设置一个属性值
    o.setField1("test-thread-scope");
    LOGGER.info("Thread object: " + o + ", field1: " + o.getField1());
    
    service.showObject();
    return CompletableFuture.completedFuture(new HashMap<>());
}

// 在showObject()中添加属性输出
public void showObject() {
    LOGGER.info("ShowObject: " + threadScopeObject + ", field1: " + threadScopeObject.getField1());
}

运行后会发现,两处输出的field1值完全一致,证明代理对象和getBean获取的实例背后是同一个线程作用域对象。


解决方案:显式配置代理模式

为了让代理行为更明确,避免混淆,建议在定义线程作用域Bean时显式指定proxyMode:

@Component
@Scope(value = "thread", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ThreadScopeObject implements InitializingBean {
    // 你的原有代码
}
  • ScopedProxyMode.TARGET_CLASS:使用CGLIB代理(适合没有实现接口的类)
  • 如果你的类实现了接口,也可以用ScopedProxyMode.INTERFACES使用JDK动态代理

配置后,无论是通过@Autowired还是getBean获取的都是代理对象,每次调用方法时都会正确获取当前线程的实例,行为会更一致。


内容的提问来源于stack exchange,提问作者saavedrah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:30:54