在Web开发中,service层或者某个工具类中需要获取到HttpServletRequest对象还是比较常见的。一种方式是将HttpServletRequest作为方法的参数从controller层一直放下传递,不过这种有点费劲,且做起来不是优雅;还有另一种则是RequestContextHolder,直接在需要用的地方使用如下方式取HttpServletRequest即可,使用代码如下:

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

要理解上面的为何可以这么使用,需要理解两个问题:

RequestContextHolder为什么能获取到当前的HttpServletRequest
HttpServletRequest是在什么时候设置到RequestContextHolder

对于第1个问题,熟悉ThreadLocal的人应该很容易看出来这个是ThreadLocal的应用,这个类的原理在上一篇博文(ThreadLocal原理)有讲到,其实很类似上篇博文文末提到的UserContextHolder。
第2个问题应该属于spring-mvc的问题,这个是在spring-mvc执行时设置进去的

源码分析
首先我们先来看下RequestContextHolder的源码,源码如下:

public abstract class RequestContextHolder  {private static final ThreadLocal<RequestAttributes> requestAttributesHolder =new NamedThreadLocal<RequestAttributes>("Request attributes");private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =new NamedInheritableThreadLocal<RequestAttributes>("Request context");public static void resetRequestAttributes() {requestAttributesHolder.remove();inheritableRequestAttributesHolder.remove();}public static void setRequestAttributes(RequestAttributes attributes) {setRequestAttributes(attributes, false);}//将RequestAttributes对象放入到ThreadLocal中,而HttpServletRequest和HttpServletResponse等则封装在RequestAttributes对象中,在此处就不对RequestAttributes这个类展开。反正我们需要知道的就是要获取RequestAttributes对象,然后再从RequestAttributes对象中获取到我们所需要的HttpServletRequest即可public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {if (attributes == null) {resetRequestAttributes();}else {if (inheritable) {inheritableRequestAttributesHolder.set(attributes);requestAttributesHolder.remove();}else {requestAttributesHolder.set(attributes);inheritableRequestAttributesHolder.remove();}}}public static RequestAttributes getRequestAttributes() {RequestAttributes attributes = requestAttributesHolder.get();if (attributes == null) {attributes = inheritableRequestAttributesHolder.get();}return attributes;}}

那么在spring-mvc中是怎么实现的呢,我们来简单分析的,想了解具体机制的可以去看看spring-mvc的源码。
我们看下FrameworkServlet这个类,也就是DispatcherServlet的父类,里面有个processRequest方法,根据方法名称我们也可以大概了解到这个是方法用于处理请求的。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {long startTime = System.currentTimeMillis();Throwable failureCause = null;LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());//将RequestAttributes设置到RequestContextHolderinitContextHolders(request, localeContext, requestAttributes);try {//具体的业务逻辑doService(request, response);}catch (ServletException ex) {failureCause = ex;throw ex;}catch (IOException ex) {failureCause = ex;throw ex;}catch (Throwable ex) {failureCause = ex;throw new NestedServletException("Request processing failed", ex);}finally {//重置RequestContextHolder之前设置RequestAttributesresetContextHolders(request, previousLocaleContext, previousAttributes);if (requestAttributes != null) {requestAttributes.requestCompleted();}if (logger.isDebugEnabled()) {if (failureCause != null) {this.logger.debug("Could not complete request", failureCause);}else {if (asyncManager.isConcurrentHandlingStarted()) {logger.debug("Leaving response open for concurrent processing");}else {this.logger.debug("Successfully completed request");}}}publishRequestHandledEvent(request, response, startTime, failureCause);}}private void initContextHolders(HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {if (localeContext != null) {LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);}if (requestAttributes != null) {RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);}if (logger.isTraceEnabled()) {logger.trace("Bound request context to thread: " + request);}}

简单看下源码,我们可以知道HttpServletRequest是在执行doService方法之前,也就是具体的业务逻辑前进行设置的,然后在执行完业务逻辑或者抛出异常时重置RequestContextHolder移除当前的HttpServletRequest。

【多线程】ThreadLocal原理
使用
在每个线程的内部有个数据结构为Map的threadLocals变量,以<ThreadLocal,Value>的形式保存着线程变量和其对应的值。
当使用set()方法时:

1.获取到当前线程的threadLocals,类型为Map
2.将这值放到这个Map结构的变量中,key为ThreadLocal对象,value为所有存放的值

当使用get()方法时:

1.获取到当前线程的threadLocals,类型为Map。
2.以ThreadLocal对象为Map的key获取到它的value值。

因为ThreadLocal对象作为Map的key,所以一个ThreadLocal对象只能存放一个值,当存放多个时,会将新值覆盖旧值。

数据结构:

public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);//当前线程为入参,获取当前线程的threadLocals变量if (map != null)//入参为this,也就是说key为ThreadLocal对象map.set(this, value);elsecreateMap(t, value);}public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);//当前线程为入参,获取当前线程的threadLocalsif (map != null) {//入参为this,也就是说key为ThreadLocalThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}ThreadLocalMap getMap(Thread t) {return t.threadLocals;//threadLocals为线程的变量}private Entry getEntry(ThreadLocal<?> key) {int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];if (e != null && e.get() == key)return e;elsereturn getEntryAfterMiss(key, i, e);//避免内存泄漏,下文有提。}

https://www.jianshu.com/p/6bf1adb775e0

RequestContextHolder获取request相关推荐

  1. Spring Boot 异步线程静态获取request对象为空 RequestContextHolder 为空 Java 异步线程获取request为空

    Spring Boot 异步线程静态获取request对象为空 RequestContextHolder 为空 Java 异步线程获取request为空 一.问题描述 在Spring Boot的web ...

  2. SpringMvc4中获取request、response对象的方法

    springMVC4中获取request和response对象有以下两种简单易用的方法: 1.在control层获取 在control层中获取HttpServletRequest和HttpServle ...

  3. springmvc获取request对象

    2019独角兽企业重金招聘Python工程师标准>>> 一.在程序中获取request非文件类型: ServletRequestAttributes attributes = (Se ...

  4. webflux切面拦截权限,webflux整合aop,webflux获取request

    背景 在springboot+tomcat应用中获取request对象可以使用RequestContextHolder.getRequestAttributes()的方式来获取,此种方式的核心在于re ...

  5. SpringMVC获取Request,Response, Session对象

    SpringMVC获取Request,Response, Session对象 方法一 通过@Resource注解注入,@Autowired也可以 @Resource private HttpServl ...

  6. springmvc中获取request对象,加载biz(service)的方法

    获取request对象: 首先配置web.xml文件--> [html] view plaincopy <listener> <listener-class> org.s ...

  7. Spring 获取 request 的几种方法及其线程安全性分析

    转载自  Spring 获取 request 的几种方法及其线程安全性分析 本文将介绍在Spring MVC开发的Web系统中,获取request对象的几种方法,并讨论其线程安全性. 一.概述 在使用 ...

  8. Spring学习总结(28)——Spring获取request对象的几种方法及线程安全性总结

    分享一个大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到人工智能的队伍中来!点击浏览教程 前言 在使用Spring MVC开发Web系统时,经常需要在处理请求时使用reque ...

  9. Spring 中获取 request 的几种方法,及其线程安全性分析

    概述 在使用Spring MVC开发Web系统时,经常需要在处理请求时使用request对象,比如获取客户端ip地址.请求的url.header中的属性(如cookie.授权信息).body中的数据等 ...

最新文章

  1. XenMobile 8.7对iOS7.1 企业级MDM管理功能带来哪些扩展?
  2. MySQL索引介绍,普通索引,全文索引,空间索引,多列索引使用原则,建立索引常用的规则
  3. 巧用PowerShell维护Win2008服务器作业
  4. java 1.7的新特性_[Java]  JDK 1.7版本的 新特性
  5. 新手赛(2) 第五题 因素和问题
  6. 用python快速将excel(含多sheet)导入MySQL(兼容中文)
  7. 第四篇: UpdatePanel 控件--触发机制Triggers
  8. linux和windows图形运算效率,linux和windows下UDP发送效率的有趣比较
  9. MS Office办公,数学公式编辑器,visio2019 详细安装教程
  10. eclipse的安装及最大子数组求和
  11. 期末复习【操作系统】
  12. cork--快速,精确的线性布尔运算
  13. 设置EditText输入的文字全部变成大写或小写方案总结
  14. python实现在图片上画框并标注
  15. 小蒋搜搜(xp search) 官网
  16. VC与VS的版本对应关系
  17. ArcGIS滑坡易发性及危险性评价实验文字说明
  18. sklearn使用实践之对moons数据集进行决策树训练并优化模型
  19. SwitchyOmega+Pac
  20. 大星星学物联网概览篇-近场通信

热门文章

  1. windows桌面便笺使用小技巧Win10便利贴不见了在哪里调出来Win10便签怎么用
  2. 第一节 、MPC5744P之系统集成单元总结 SIUL2(System Integration Unit Lite2 )介绍
  3. ORACLE找出最大的数返回 GREATEST()函数
  4. Cisco服务器怎么安装系统,Cisco MCS服务器上Microsoft操作系统补丁安装发生故障
  5. Learning English From Android Source Code:1
  6. 赛门铁克一体机后来居上
  7. Roxe:下一代全球支付网络
  8. Python实现鸡兔同笼问题
  9. 机器学习面试宝典200题!
  10. 快讯| RStudio Connect 发布