原文网址:SpringBoot--@Autowired注入HttpServletRequest是否线程安全?_IT利刃出鞘的博客-CSDN博客
简介
本文用实例结合源码来说明@Autowired注入HttpServletRequest是线程安全的。
SpringBoot获取HttpServletRequest有多种方式,见:SpringBoot--获取request(HttpServletRequest)的方法_IT利刃出鞘的博客-CSDN博客,其中一种方法是:在Controller中使用@Autowired注入HttpServletRequest,它是一个公共的变量,肯定就要怀疑它的线程安全性:多个请求同时进来时,使用这个request是线程安全的吗?
结论是:是线程安全的。
实例
代码
package com.knife.example.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@Slf4j
public class HelloController {
@Autowired
private HttpServletRequest request;
@GetMapping("/test")
public String test(String id) {
request.setAttribute("id", id);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("time值:{},线程名:{},request地址:{}",
request.getAttribute("id"),
Thread.currentThread().getName(),
request);
return "success";
}
}
测试
用jmeter发起10个并发请求,请求的url是:http://localhost:8080/test?id=1
(请求的id从1到10)
结果:
2023-05-11 17:51:57.045 INFO 5348 --- [nio-8080-exec-9] c.k.example.controller.HelloController : time值:1,线程名:http-nio-8080-exec-9,request地址:Current HttpServletRequest
2023-05-11 17:51:57.045 INFO 5348 --- [nio-8080-exec-1] c.k.example.controller.HelloController : time值:3,线程名:http-nio-8080-exec-1,request地址:Current HttpServletRequest
2023-05-11 17:51:57.045 INFO 5348 --- [nio-8080-exec-4] c.k.example.controller.HelloController : time值:2,线程名:http-nio-8080-exec-4,request地址:Current HttpServletRequest
2023-05-11 17:51:57.053 INFO 5348 --- [nio-8080-exec-3] c.k.example.controller.HelloController : time值:8,线程名:http-nio-8080-exec-3,request地址:Current HttpServletRequest
2023-05-11 17:51:57.053 INFO 5348 --- [nio-8080-exec-2] c.k.example.controller.HelloController : time值:9,线程名:http-nio-8080-exec-2,request地址:Current HttpServletRequest
2023-05-11 17:51:57.053 INFO 5348 --- [io-8080-exec-10] c.k.example.controller.HelloController : time值:6,线程名:http-nio-8080-exec-10,request地址:Current HttpServletRequest
2023-05-11 17:51:57.053 INFO 5348 --- [nio-8080-exec-5] c.k.example.controller.HelloController : time值:4,线程名:http-nio-8080-exec-5,request地址:Current HttpServletRequest
2023-05-11 17:51:57.053 INFO 5348 --- [nio-8080-exec-7] c.k.example.controller.HelloController : time值:10,线程名:http-nio-8080-exec-7,request地址:Current HttpServletRequest
2023-05-11 17:51:57.053 INFO 5348 --- [io-8080-exec-11] c.k.example.controller.HelloController : time值:7,线程名:http-nio-8080-exec-11,request地址:Current HttpServletRequest
2023-05-11 17:51:57.053 INFO 5348 --- [nio-8080-exec-6] c.k.example.controller.HelloController : time值:5,线程名:http-nio-8080-exec-6,request地址:Current HttpServletRequest
可以发现:在同一时间虽然有多个请求进来,但使用request是线程安全的。
源码分析
可以打断点跟踪源码,本处贴出流程:
简要描述其流程:
通过@Autowired注入的Request对象,其实并非是原生的HttpServletRequest对象,而是由Spring通过JDK动态代理技术生成的一个代理对象。
代理对象只是一个空壳,本身不具备功能,所有的操作都让RequestObjectFactory.getObject()返回的对象去处理了。而RequestObjectFactory.getObject()底层就是从RequestContextHolder的ThreadLocal变量requestAttributesHolder获取的。
说白了,从代码上看似是多个线程并发操作一个共享变量request,其实Spring底层通过一个代理对象让客户端去操作了ThreadLocal中的request,即每个线程都只操作自己的request,是线程隔离的,所以不存在并发安全问题。