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
相关产品推荐
相关产品推荐

