RequestContextHolder详解

最近遇到的问题是在service获取request和response,正常来说在service层是没有request的,然而直接从controlller传过来的话解决方法太粗暴,后来发现了SpringMVC提供的RequestContextHolder遂去分析一番,并借此对SpringMVC的结构深入了解一下,后面会再发文章详细分析源码

1.RequestContextHolder的使用

RequestContextHolder顾名思义,持有上下文的Request容器.使用是很简单的,具体使用如下:

复制代码

 
  1. //两个方法在没有使用JSF的项目中是没有区别的

  2. RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();

  3. //RequestContextHolder.getRequestAttributes();

  4. //从session里面获取对应的值

  5. String str = (String) requestAttributes.getAttribute("name",RequestAttributes.SCOPE_SESSION);

  6. HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();

  7. HttpServletResponse response = ((ServletRequestAttributes)requestAttributes).getResponse();

复制代码

看到这一般都会想到几个问题:

  1. request和response怎么和当前请求挂钩?
  2. request和response等是什么时候设置进去的?

2.解决疑问

2.1 request和response怎么和当前请求挂钩?

首先分析RequestContextHolder这个类,里面有两个ThreadLocal保存当前线程下的request,关于ThreadLocal可以参考我的另一篇博文[Java学习记录--ThreadLocal使用案例]

复制代码

 
  1. //得到存储进去的request

  2. private static final ThreadLocal<RequestAttributes> requestAttributesHolder =

  3. new NamedThreadLocal<RequestAttributes>("Request attributes");

  4. //可被子线程继承的request

  5. private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =

  6. new NamedInheritableThreadLocal<RequestAttributes>("Request context");

复制代码

再看`getRequestAttributes()`方法,相当于直接获取ThreadLocal里面的值,这样就保证了每一次获取到的Request是该请求的request.

复制代码

 
  1. public static RequestAttributes getRequestAttributes() {

  2. RequestAttributes attributes = requestAttributesHolder.get();

  3. if (attributes == null) {

  4. attributes = inheritableRequestAttributesHolder.get();

  5. }

  6. return attributes;

  7. }

复制代码

2.2request和response等是什么时候设置进去的?

找这个的话需要对springMVC结构的`DispatcherServlet`的结构有一定了解才能准确的定位该去哪里找相关代码.

在IDEA中会显示如下的继承关系.

左边1这里是Servlet的接口和实现类.

右边2这里是使得SpringMVC具有Spring的一些环境变量和Spring容器.类似的XXXAware接口就是对该类提供Spring感知,简单来说就是如果想使用Spring的XXXX就要实现XXXAware,spring会把需要的东西传送过来.

那么剩下要分析的的就是三个类,简单看下源码

1. HttpServletBean 进行初始化工作

2. FrameworkServlet 初始化 WebApplicationContext,并提供service方法预处理请

3. DispatcherServlet 具体分发处理.

那么就可以在FrameworkServlet查看到该类重写了service(),doGet(),doPost()...等方法,这些实现里面都有一个预处理方法`processRequest(request, response);`,所以定位到了我们要找的位置

查看`processRequest(request, response);`的实现,具体可以分为三步:

  1. 获取上一个请求的参数
  2. 重新建立新的参数
  3. 设置到XXContextHolder
  4. 父类的service()处理请求
  5. 恢复request
  6. 发布事

复制代码

 
  1. protected final void processRequest(HttpServletRequest request, HttpServletResponse response)

  2. throws ServletException, IOException {

  3. long startTime = System.currentTimeMillis();

  4. Throwable failureCause = null;

  5. //获取上一个请求保存的LocaleContext

  6. LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();

  7. //建立新的LocaleContext

  8. LocaleContext localeContext = buildLocaleContext(request);

  9. //获取上一个请求保存的RequestAttributes

  10. RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();

  11. //建立新的RequestAttributes

  12. ServletRequestAttributes requestAttributes = buildRequestAttributes(request,

  13. response, previousAttributes);

  14. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

  15. asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),

  16. new RequestBindingInterceptor());

  17. //具体设置的方法

  18. initContextHolders(request, localeContext, requestAttributes);

  19. try {

  20. doService(request, response);

  21. }

  22. catch (ServletException ex) {

  23. failureCause = ex;

  24. throw ex;

  25. }

  26. catch (IOException ex) {

  27. failureCause = ex;

  28. throw ex;

  29. }

  30. catch (Throwable ex) {

  31. failureCause = ex;

  32. throw new NestedServletException("Request processing failed", ex);

  33. }

  34. finally {

  35. //恢复

  36. resetContextHolders(request, previousLocaleContext, previousAttributes);

  37. if (requestAttributes != null) {

  38. requestAttributes.requestCompleted();

  39. }

  40. if (logger.isDebugEnabled()) {

  41. if (failureCause != null) {

  42. this.logger.debug("Could not complete request", failureCause);

  43. }

  44. else {

  45. if (asyncManager.isConcurrentHandlingStarted()) {

  46. logger.debug("Leaving response open for concurrent processing");

  47. }

  48. else {

  49. this.logger.debug("Successfully completed request");

  50. }

  51. }

  52. }

  53. //发布事件

  54. publishRequestHandledEvent(request, response, startTime, failureCause);

  55. }

  56. }

复制代码

再看initContextHolders(request, localeContext, requestAttributes)方法,把新的RequestAttributes设置进LocalThread,实际上保存的类型为ServletRequestAttributes,这也是为什么在使用的时候可以把RequestAttributes强转为ServletRequestAttributes.

复制代码

 
  1. private void initContextHolders(HttpServletRequest request,

  2. LocaleContext localeContext,

  3. RequestAttributes requestAttributes) {

  4. if (localeContext != null) {

  5. LocaleContextHolder.setLocaleContext(localeContext,

  6. this.threadContextInheritable);

  7. }

  8. if (requestAttributes != null) {

  9. RequestContextHolder.setRequestAttributes(requestAttributes,

  10. this.threadContextInheritable);

  11. }

  12. if (logger.isTraceEnabled()) {

  13. logger.trace("Bound request context to thread: " + request);

  14. }

  15. }

复制代码

因此RequestContextHolder里面最终保存的为ServletRequestAttributes,这个类相比`RequestAttributes`方法是多了很多.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

守护 C 盘,Python 相关库设置

前言 pip 安装依赖和 conda 创建环境有多方便&#xff0c;那 C 盘就塞得就有多满。以前我不管使用什么工具&#xff0c;最多就设置个安装位置&#xff0c;其他都是默认。直到最近 C 盘飙红了&#xff0c;我去盘符里的 AppData 里一看&#xff0c;pip 的缓存和 conda 以前创建的…

物联网AI MicroPython学习之语法 GPIO输入输出模块

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; GPIO 介绍 模块功能: GPIO通用输入输出。 接口说明 GPIO - 构建GPIO对象 函数原型&#xff1a;Pin(port, dir , pull)参数说明&#xff1a; 参数类型必选参数&#xff1f;说明portintY对应开发板的引脚号…

Debezium-Embedded 实时监控MySQL数据变更

1.Debezium-Embedded 简介 Debezium连接器的操作通常是将它们部署到Kafka Connect服务&#xff0c;并配置一个或多个连接器来监控上游数据库&#xff0c;并为它们在上游数据库中看到的所有更改生成数据更改事件。这些数据更改事件被写入Kafka&#xff0c;在那里它们可以被许多不…

JavaEE进阶学习:Spring 的创建和使用

Spring 就是⼀个包含了众多工具方法的 IoC 容器。既然是容器那么它就具备两个最基本的功能&#xff1a; 将对象存储到容器&#xff08;Spring&#xff09;中从容器中将对象取出来 接下来使用 Maven 方式来创建一个 Spring 项目&#xff0c;创建 Spring 项目和 Servlet 类似&a…

淘宝客APP源码/社交电商自营商城源码/前端基于Uniapp开发

淘宝客APP源码&#xff0c;前端基于Uniapp开发的社交电商自营商城源码。Thinkphp的后台&#xff0c;不是很标准&#xff0c;感兴趣的可以自行研究。 商城功能 1、首页基础装修&#xff1b;2、丰富选品库&#xff1b;3、淘口令解析&#xff1b;4、支持京东&#xff1b;5、支持…

免费IDEA插件分享:Apipost-Helper

今天给大家推荐一款IDEA插件&#xff1a;Apipost-Helper-2.0&#xff0c;写完代码IDEA内一键生成API文档&#xff0c;无需安装、打开任何其他软件&#xff1b;写完代码IDEA内一键调试&#xff0c;无需安装、打开任何其他软件&#xff1b;生成API目录树&#xff0c;双击即可快速…

day18_多线程

今日内容 零、 复习昨日 一、作业 二、线程安全的集合 三、死锁 四、线程通信 五、线程池 零、 复习昨日 见晨考 一、线程安全[重点] 1.0 线程不安全 当前线程的数据被其他线程修改 1.1 线程安全 临界资源&#xff1a;共享资源(同⼀个对象)&#xff0c;一次只可以有一个线程操…

cudnn安装

安装地址 cudnn安装地址&#xff1a;https://developer.nvidia.com/rdp/cudnn-download 安装 选择windows版本的下载&#xff0c;我这里选择的这个&#xff1a; 下载之后解压即可。 后续 后续&#xff1a;第一步 把cudnn的bin&#xff0c;include&#xff0c;lib三个文件…

k8s之Helm

理论&#xff1a; 什么是 He lm 在没使用 helm 之前&#xff0c;向 kubernetes 部署应用&#xff0c;我们要依次部署 deployment、svc 等&#xff0c;步骤较繁琐。 况且随着很多项目微服务化&#xff0c;复杂的应用在容器中部署以及管理显得较为复杂&#xff0c;helm 通过打包…

树莓派连接NOKOV接收动作捕捉数据:通过SDK进行

NOKOV度量动作捕捉系统支持通过SDK与树莓派通信&#xff0c;将动作捕捉数据实时传输给树莓派。 一、形影软件设置 1、首先打开形影动捕软件&#xff0c;加载一段后处理数据。这里选择了一段小车飞机的协同数据。 在这个数据里面可以看到&#xff0c;整个场景里面有两个刚体&a…

JS-项目实战-代码优化-事件动态绑定

1、鼠标悬浮和离开事件.js //当页面加载完成后执行后面的匿名函数 window.onload function () {//get:获取 Element:元素 By:通过...方式//getElementById()根据id值获取某元素let fruitTbl document.getElementById("fruit_tbl");//table.rows:获取这个表格…

零代码数字孪生设计平台的功能特点

在当今数字化的时代&#xff0c;企业的转型已经成为必然的趋势。而在这个过程中&#xff0c;3D数字孪生无代码编辑工具正成为企业实现数字化转型的新价值点。客户可以无需任何专业知识和专业软件的支持&#xff0c;仅仅通过互联网和浏览器即可根据购买要求对自己的产品/设备/园…

RGB转Bayer,一个小数点引发的血案

前几天写了一个RGB数据转Bayer格式的函数&#xff0c;经过测试功能正常。后来把这个函数用到一个数据库构建中&#xff0c;结果数据库出来的结果一直是一张黑图&#xff0c;追查了好几个小时&#xff0c;总算把这只虫子找出来了&#xff0c;原来是一个整数后面的小数点作祟。 …

Flume(一)【Flume 概述】

前言 今天实在不知道学点什么好了&#xff0c;早上学了3个多小时的 Flink &#xff0c;整天只学一门技术是很容易丧失兴趣的。那就学点新的东西 Flume&#xff0c;虽然 Kafka 还没学完&#xff0c;但是大数据生态圈的基础组件也基本就剩这倆了。 Flume 概述 生产环境中的数据一…

最佳实践-使用Github Actions来构建跨平台容器镜像

概述 GitHub Actions 是一种持续集成和持续交付 (CI/CD) 平台&#xff0c;可用于自动执行生成、测试和部署管道。 您可以创建工作流程来构建和测试存储库的每个拉取请求&#xff0c;或将合并的拉取请求部署到生产环境。 GitHub Actions 不仅仅是 DevOps&#xff0c;还允许您在存…

【评论送书】十本架构师成长和软件架构技术相关的好书(可以任选)

正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 参与规则 本次送书1~5本参与方式&#xff1a;关注博主、点赞、收藏、评论&#xff08;从评论区…

asp.net core mvc之 过滤器

过滤器允许我们在Action执行之前和执行之后去执行一下业务代码 一、过滤器的作用域 1、全局过滤器&#xff0c; 在Startup.cs文件中注册 2、控制器过滤器&#xff0c; 在控制器类上面使用标注 3、action过滤器 二、全局过滤器使用 1、在 core 目录&#xff0c;添加 TestA…

选择墨西哥专线双清包税前需要了解什么?

选择墨西哥专线双清包税前&#xff0c;需要了解以下几个方面的情况&#xff1a; 1. 墨西哥市场的需求和特点&#xff1a;首先需要了解墨西哥的市场需求和特点&#xff0c;包括消费者对于产品的偏好、消费习惯、购买力等。对于汽配零件行业来说&#xff0c;需要了解墨西哥汽车市…

@postmapping 定义formdata传参方式

背景&#xff1a;feign声明接口&#xff0c;传对象&#xff0c; 但是对象那边没有用requestBody接收&#xff1b; 前端调它也是走的formdata&#xff0c;所以不改变源代码&#xff0c;以及补新接口的情况下&#xff0c;我也需要formdata传参&#xff1b; 不然数据传不过去会为空…