HttpServletRequest在Spring中的获取和注入 @Autowired注入Request

news2024/10/6 8:38:59

问题描述:

在最近一次团队review代码时,团队成员发现有将HttpServletRequest 直接通过@Autowired注入的情况,于是大家产生了一个疑问,HttpServletRequest并非Spring中的类,且在没有手动通过@Bean的方式注入,Spring是怎么做到帮开发者完成注入的?

同时,我们知道ioc容器中默认注入的Bean是单例,而每个请求都是独立的,这样不会出问题吗?

为了研究明白为什么,我找了些资料,同时写了个简单的demo研究了下。

Demo类如下:

/**
 * @author zhangrenwei
 * @date 2022/8/1
 * @description replace this
 **/
@RestController
public class HttpServletRequestTest {

    @Autowired
    private HttpServletRequest httpServletRequest;

    @GetMapping("/sayHi")
    public void sayHi(String name) {

        System.out.println(httpServletRequest.getRequestURL().toString());
        JSONObject res = new JSONObject();
        res.put("requestId", UUID.randomUUID().toString());
        res.put("datatime", DateUtils.getDatetime());
        // ReturnResult.get(res);

        System.out.println("hello: " +  name);
    }

    @PostConstruct
    public void after(){
        System.out.println(this.httpServletRequest);
    }

}

疑虑1

作为一个外部对象,HttpServletRequest为什么可以在Spring项目中通过注入的方式获取?

The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization. This method MUST be invoked before the class is put into service. This annotation MUST be supported on all classes that support dependency injection.

由关于@PostConstruct的描述知,@PostConstruct注解被用在执行完依赖注入之后的方法调用上,我们将断点打在上述demo的第19行,即可查看HttpServletRequest httpServletRequest实例化之后的情况。

由上图我们可以看到这个httpServletRequest对象是一个代理对象(org.springframework.web.context.support.WebApplicationContextUtils.RequestObjectFactory),该对象是一个请求的对象工厂。进入WebApplicationContextUtils这个类,Command + F12我们发现了一个往 ConfigurableListableBeanFactory工厂对象注入bean对象的方法registerWebApplicationScopes。

该方法有这样一行代码,beanFactory.``registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()``);

由于HttpServletRequest正是继承自ServletRequest,这里引起了我们的关注。

ConfigurableListableBeanFactory的registerResolvableDependency方法又是用什么的呢?我们继续往下看。

使用相应的自动装配值注册一个特殊的依赖类型。

这适用于被认为可自动装配但未在工厂中定义为 bean 的工厂/上下文引用

看到这里我们就明白了,原来Spring可以为一个类注入另一个完全不同的对象值,这样从ioc容器中引用这个类的时候其实拿到的就是这个对象值。且上面的描述正好回应了,为什么我们没有手动定义HttpServletRequest却可以完成它的自动装配,秘诀就在这里。(进一步拓展思考,上面两张图一起看,除了ServletRequest.class,ServletResponse.class,HttpSession.class以及WebRequest.class类型对象,均被Spring自动注入,可以直接通过注解的方式引用)。

继续进入registerResolvableDependency方法,我们发现该方法的实现是将ServletRequest.class的依赖类型作为key, RequestObjectFactory作为装配的value,放入了resolvableDependencies这个map中。

疑虑2

HttpServletRequest注入和引用我们知道了,那么针对每个独立的请求,多线程场景下,通过自动注入的方式,HttpServletRequest 是否会有线程安全的问题呢?

我们已经知道注入拿到的HttpServletRequest返回的对象其实是下面这个

org.springframework.web.context.support.WebApplicationContextUtils.``RequestObjectFactory对象,那么它是怎么做到线程安全的呢?

我们看它的具体实现。

沿着currentRequestAttributes()方法一路点击,最终发现它的返回值,是来自org.springframework.web.context.request.RequestContextHolder#requestAttributesHolder。

requestAttributesHolder对象是一个ThreadLocal对象,由此我们明白了,Spring自动注入的HttpServletRequest能够保证请求的唯一性,原来是通过跟每个线程绑定了,从ThreadLocal中取得请求信息,由此保证的线程安全!

我们再来拓展一下,既然请求信息是从ThreadLocal中取的,那么请求信息是如何放进去的呢?

我们找到requestAttributesHolder属性的set方法,在这里打一个断点,重启系统,并发起一个http请求。

最终,通过调用堆栈信息,我们看到了org.springframework.web.filter.RequestContextFilter#doFilterInternal

中调用了initContextHolder方法,而initContextHolder方法调用了requestAttributesHolder这个ThreadLocal的set方法。

每个http请求过来会进入RequestContextFilter这个filter,在这个filter中会将request信息设置到当前的线程里。到这里,所有的谜团都解开了。

总结

我们来做一个总结,在代码中通过注解注入HttpServletRequest的方式,拿到的其实并不是真正的 HttpServletRequest,而是一个Spring项目启动时自动注入的代理对象org.springframework.web.context.support.``WebApplicationContextUtils.RequestObjectFactory。

该对象跟ServletRequest.class做了映射关联,放入了Spring管理bean注入的map中。

每次请求时,如何保证自动注入的HttpServelet请求线程安全,等价于问 org.springframework.web.context.support.WebApplicationContextUtils.``RequestObjectFactory 怎么做到的线程安全。这个类只是一个简单的对象工厂,getObject方法最终从org.springframework.web.context.request.RequestContextHolder#requestAttributesHolder这个ThreadLocal中获取请求的信息,由此做到了请求之间的隔离。

参考:链接1,链接2


如果本篇文章对你有帮助的话,很高兴能够帮助上你。

当然,如果你觉得文章有什么让你觉得不合理、或者有更简单的实现方法又或者有理解不来的地方,希望你在看到之后能够在评论里指出来,我会在看到之后尽快的回复你。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/493159.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Oracle数据库、实例、用户、表空间、表之间的关系

数据库: Oracle数据库是数据的物理存储。这就包括(数据文件ORA或者DBF、控制文件、联机日志、参数文件)。其实Oracle数据库的概念和其它数据库不一样,这里的数据库是一个操作系统只有一个库。可以看作是Oracle就只有一个大数据库。…

Vue核心 绑定样式 条件渲染

1.11.绑定样式 class样式: 写法::class“xxx”,xxx 可以是字符串、数组、对象:style“[a,b]” 其中a、b是样式对象**:style“{fontSize: xxx}”**其中 xxx 是动态值 字符串写法适用于:类名不确定,要动态获取数组写法…

HTB靶机07-Cronos-WP

cronos IP:10.10.10.13 scan ┌──(xavier㉿kali)-[~] └─$ sudo nmap -sSV -T4 10.10.10.13 Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-06 23:19 CST Nmap scan report for 10.10.10.13 Host is up (0.23s latency). Not shown: 997 closed tcp por…

SpringCloud全面学习笔记之进阶篇

目录 前言微服务保护初识Sentinel雪崩问题及解决方案雪崩问题超时处理仓壁模式熔断降级流量控制总结 服务保护技术对比Sentinel介绍和安装微服务整合Sentinel 流量控制快速入门流控模式关联模式链路模式小结 流控效果warm up排队等待 热点参数限流全局参数限流热点参数限流案例…

算法记录 | Day52 动态规划

300.最长递增子序列 思路: 1.dp[i]的定义:以 nums[i] 结尾的最长递增子序列长度。 2.状态转移方程:位置i的最长升序子序列等于j从0到i-1各个位置的最长升序子序列 1 的最大值。 if (nums[i] > nums[j]) dp[i] max(dp[i], dp[j] 1); 注意这里不是要dp[i] …

基于AT89C52单片机的电子秒表设计与仿真

点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/87755619?spm1001.2014.3001.5503 源码获取 主要内容: 本设计以AT89C52单片机为核心,采用常用电子器件设计,包括电源开关、按键…

网络安全 等级保护 网络设备、安全设备知识点汇总

网络设备、安全设备知识点汇总 1、防火墙(Firewall) 定义:相信大家都知道防火墙是干什么用的, 我觉得需要特别提醒一下,防火墙抵御的是外部的攻击,并不能对内部的病毒 ( 如ARP病毒 ) 或攻击没什么太大作用。 功能:防火墙的功能…

coturn中turnutils_peer和turnutils_uclient使用说明

coturn的作用有两个:寻找反射地址以及流转发,本人写过webrtc janus服务器部署在公网,coturn转发媒体流 coturn下面的工具turnutils_stunclient用于查找反射地址。 而turnutils_peer和turnutils_uclient用于测试转发功能,再次给以…

STL中priority_queue自定义类型使用和源码简单分析

priority_queue使用 这里说一下优先级队列的其他的用法,这里我们先看默认的究竟是建立大堆还是小堆? #include <iostream> #include <queue>int main() {int arr[] { 10, 2, 1, 3, 5, 4, 0 };std::priority_queue<int> q;for (size_t i 0; i < sizeo…

基于springboot的私人健身与教练预约管理系统

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代…

计算机网络学习笔记-概述

目录 信息时代 互联网 因特网发展的三个阶段 制定互联网的正式标准阶段 互联网组成&#xff1a;边缘部分核心部分 边缘部分 核心部分 计算机网络 体系结构 OSI 七层参考模型&#xff1a;物理层 数据链路层 网络层 运输层 会话层 表示层 应用层 TCP/IP 4层参考模型&a…

【K8S系列】深入解析k8s网络

序言 你只管努力&#xff0c;其他交给时间&#xff0c;时间会证明一切。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用来标记二级论点 Kubernetes (k8s) 是一个容器编排平台&#x…

(8) 支持向量机分类器SVC案例:预测明天是否会下雨

文章目录 案例介绍1 导库导数据&#xff0c;探索特征2 分集&#xff0c;优先探索标签3 探索特征&#xff0c;开始处理特征矩阵3.1 描述性统计与异常值3.2 处理困难特征&#xff1a;日期3.3 处理困难特征&#xff1a;地点3.4 处理分类型变量&#xff1a;缺失值3.5 处理分类型变量…

Typora + PicGo + Gitee 搭建免费图床

搭建准备 本次搭建过程需要以下介质&#xff1a;Typora PicGo Gitee/GitHub &#xff0c;「免费&#xff01;」 Typora Typora 是一款 markdown 编辑器&#xff0c;支持几乎所有的 markdown 格式&#xff0c;神器&#xff01; 支持 macOS、Windows、Linux 三种操作系统&am…

Composition API 的优势、新的组件(Fragment,Teleport,Suspense)【Vue3】

四、Composition API 的优势 1. Options API 存在的问题 使用传统OptionsAPI中&#xff0c;新增或者修改一个需求&#xff0c;就需要分别在data&#xff0c;methods&#xff0c;computed里修改。 2. Composition API 的优势 我们可以更加优雅的组织我们的代码&#xff0c…

Packet Tracer - 在思科路由器上配置 AAA 认证

Packet Tracer - 在思科路由器上配置 AAA 认证 拓扑图 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 交换机端口 R1 G0/1 192.168.1.1 255.255.255.0 不适用 S1 F0/1 S0/0/0 (DCE) 10.1.1.2 255.255.255.252 不适用 不适用 R2 G0/0 192.168.2.1 255.2…

(4)基本组件

目录 1. Designer 设计师** 2. Layout 布局*** 3. 基本组件 3.1 QWidget** 3.2 ui指针 3.3 QLabel 标签** 3.4 QAbstractButton 按钮类** 示例 1. Designer 设计师** Designer是一款独立的用于设计Qt界面的应用程序。 Designer程序保存的文件格式为.ui&#xff0c;这是Qt中的界…

腾讯云轻量应用服务器修改镜像系统有哪些限制?

腾讯云轻量应用服务器镜像可以更换或修改吗&#xff1f;可以&#xff01;镜像可以修改&#xff0c;镜像是指轻量服务器的预装操作系统&#xff0c;轻量服务器创建成功后镜像也是可以更换的。 镜像是轻量服务器的预装操作系统&#xff0c;轻量应用服务器的镜像不仅包含操作系统&…

看我如何通过帮助服务台轻松黑掉数百家公司

导语&#xff1a;几个月前&#xff0c;我发现黑客可以利用一个漏洞访问目标公司的内部通信。 这个漏洞只需要点击几下&#xff0c;就可以访问企业内部网络、 Twitter等社交媒体账户&#xff0c;以及最常见的Yammer和Slack团队。 更新: The Next Web 写了一篇我发现的这个漏洞的…

SAP BusinessObjects BI crack

SAP BusinessObjects BI crack 通过基于驱动程序标准的数据库、报告工具&#xff0c;甚至自定义应用程序&#xff0c;加入SAP BusinessObjects BI报告和分析。 与BI分析、报告、ETL工具和自定义解决方案集成。 SAP BusinessObjects BI奇妙功能中的CData驱动程序&#xff1a; BI…