以下是一个 Spring Boot 的 Controller
示例,展示了两种不同的请求对象注入方式(字段注入 vs. 方法参数注入),并分析它们在并发场景下的行为差异:
java复制@RestController
public class RequestInjectController {
@Autowired // 字段注入:潜在并发问题
private HttpServletRequest request1;
@GetMapping("/fieldInject")
public String fieldInject() {
return request1.getQueryString();
}
@GetMapping("/methodInject")
public String methodInject(HttpServletRequest request2) { // 方法参数注入:线程安全
return request2.getQueryString();
}
}
问题:这段代码的字段注入会不会出现图中右边的问题呢?

先说答案,不会!为什么呢?先来看一下字段注入和方法注入的request对象有什么区别


可以看到,request1
注入的实例是一个代理,而request2
注入的是实际由tomcat
创建的RequestFacade
对象。request1
注入的代理对象实际上是单例的,所以不会有上面图片中的字段并发覆盖问题。那么新的问题又来了,request1
是怎么同一个对象(单例代理对象)做到可以隔离不同请求的request呢?
就拿现在的request1.getQueryString()
来看,我们debug进入一下这个代理对象做了什么
直接打上断点单步进入。可以看到,进入到了一个动态代理的拦截器中,而调用的具体对象是通过objectFactory.getObject()
获取的。

我们继续进入看看objectFactory.getObject()
做了什么。

这个objectFactory
的具体类型是RequestObjectFactory
。他会使用currentRequestAttributes().getRequest()
获得具体的请求对象,用来执行我们需要的getQueryString()
调用。那么currentRequestAttributes()
做了什么呢?

他会通过RequestContextHolder.currentRequestAttributes()获取当前的请求上下文属性,最终就到这里了

可以看到他是从ThreadLocal中获取的,最终是获取到的request对象是每个线程直接隔离的。
