SpringBoot--@Autowired注入HttpServletRequest是否线程安全?

发布于:2024-05-18 ⋅ 阅读:(199) ⋅ 点赞:(0)

原文网址: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,是线程隔离的,所以不存在并发安全问题。


网站公告

今日签到

点亮在社区的每一天
去签到