2019独角兽企业重金招聘Python工程师标准>>>

作者:wangxinxi

最近有人问我,Spring MVC Controller的成员变量@Resource private HttpServletRequest request,这样用会不会产生线程安全问题。咋一想Spring MVC 的 Controller默认是单例,成员变量request应该会导致线程安全问题,那么真的是这样的?

已知一个Spring MVC的Controller, 用户的控制器

代码如下:

@Controller

@RequestMapping("/user")

public class UserController {

private HttpServletRequest request1;

@Resource

private HttpServletRequest request2;

@ModelAttribute

public void setRequest1(HttpServletRequest request) {

this.request1 = request;

}

@RequestMapping("/test")

public void testRequest(){

System.out.println(request1.getParameter("test"));

System.out.println(request2.getParameter("test"));

}

那么request1和request2, 在高并发下是线程安全性的?

先说答案:request1非线程安全,而request2是线程安全

众所周知,在 Controller 是单例的情况下,成员变量一般不是线程安全的(即多个线程共享一个成员变量), 但是request2却是线程安全的, 为什么?

request1非线程安全我们丝毫不怀疑:

假设有用户甲和乙同事并发的访问user/test接口,甲和乙都会把自己的request赋值给request1。当甲使用request1的时候, 乙可以把乙的request赋值给成员变量request1,但是甲这时候使用的就是乙的request了,就有可能导致安全问题。

但是request2仅仅加上了一个注解"@Resource"就变成了线程安全的。

所以我决定分析一下,下图是用idea调试模式,很容易发现, 接口request1的 多态是基于WebAPP容器的RequestFacade,对与当前的环境具体是指Tomcat对HttpServletRequest 的门面模式的实习;接口request2是$Proxy15,这个是啥东西?明眼的人一眼就看出了, 这个是基于JDK动态代理生成的代理类,生成代理类型的规则是$ProxyN, 如果不知道JDK的动态代理请自行Google。

继续分析request2,代理类的InvocationHandler是AutowireUtils的内部类ObjectFactoryDelegatingInvocationHandler,这一切都是怎么发生的?

寻踪溯源

1、Spring 实例化Bean的时候,发现有一个@Resource private HttpServletRequest request2;需要注入,需要进行注入处理

如果类型是

这8种接口, 需要特殊处理,包含javax.servlet.http.HttpServletRequest,被映射成WebApplicationContextUtils.RequestObjectFactory这个是生产Request的工厂类,这个工厂就是生产ServletRequest的,其源代码如下:

然后通过JDK动态代理生成HttpServletRequest的代理类

ObjectFactoryDelegatingInvocationHandler 动态代理的具体实现

最后完成对request2注入。

2、粗略看注入流程,除了特殊处理HttpServletRequest之外没有什么特殊的。

然而我们在看看WebApplicationContextUtils.RequestObjectFactory的getObject()方法

通过currentRequestAttributes方法拿到了ServletRequestAttributes,最后通过ServletRequestAttributes拿到了HttpServletRequest。

下面我们分析一下currentRequestAttributes方法

接着调用了RequestContextHolder.currentRequestAttributes();

然后调用了

最后是通过requestAttributesHolder拿到了HttpServletRequest。

我们再来看看requestAttributesHolder是什么鬼?

我们惊奇的发现requestAttributesHolder是ThreadLocal<RequestAttributes>,有了ThreadLocal这个神器,RequestAttributes绑定着了HttpServletRequest,难怪可以保证@Resource private HttpServletRequest request2;这个线程安全,自此真相大白。

RequestAttributes是如何绑定HttpServletRequest的?

在web.xml配置的监听器

<listener>

<listener-class>

org.springframework.web.context.request.RequestContextListener

</listener-class>

</listener>

通过HTTP请求时的监听器进行的配置,在这个请求的上下文中都可以得到这个request

这里讲一下,ThreadLocal的作用是提供线程内的局部变量,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性。更多关于ThreadLocal知识请自行google。

简而言之,Spring MVC在 Controller 是单例的情况下,会对HttpServletRequest等需要注入的接口做特殊处理,通过JDK的动态代理的方式和ThreadLocal对应的线程变量绑定,从而保证线程安全。所以在Controller等其他的请求上下文中放心的使用@Resource private HttpServletRequest request吧。

希望对您有所帮助

转载于:https://my.oschina.net/u/270991/blog/1785896

Spring MVC 成员变量 request 线程安全问题的讨论相关推荐

  1. spring MVC中获取request和response

    spring MVC中获取request和response: Java代码   HttpServletRequest request = ((ServletRequestAttributes) Req ...

  2. 线程之成员变量的线程共享

    import java.util.HashMap;import java.util.Map;import java.util.Random; public class ThreadScopeShare ...

  3. Spring MVC中获取Request的方法及分析

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

  4. Spring MVC中处理Request和Response的策略

    前沿技术早知道,弯道超车有希望 积累超车资本,从关注DD开始 作者:码农小胖哥, 图文编辑:xj 来源:https://mp.weixin.qq.com/s/3eFygsiVl8dC2nRy8_8n5 ...

  5. Java中实例变量的线程安全问题的分析

    public class Test{private int count = 5;//模拟库存量public void reduceStock(){//模拟减少库存count--;System.out. ...

  6. servlet单实例多线程 ---线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。(所有建议不要在servlet中定义成员变

    Servlet 单例多线程 Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servl ...

  7. Java并发编程(8)——常见的线程安全问题

    线程安全问题: 多个线程同时执行也能工作的代码就是线程安全的代码 如果一段代码可以保证多个线程访问的时候正确操作共享数据,那么它是线程安全的. 具体说明: java并发线程实战(1) 线程安全和机制原 ...

  8. java 全局变量线程安全_Java线程安全问题指的是全局变量,还是静态变量?

    这个问题的答案是静态变量和全局变量都可能引起线程安全问题.这两种变量引起线程安全问题的原因和区别如下: 1.静态变量 静态变量即静态成员变量.只要有修改变量值的操作,无论是在单例或者非单例都是线程不安 ...

  9. JUC线程安全问题阶段二

    线程问题 4.1 线程出现问题的根本原因分析 线程出现问题的根本原因是因为线程上下文切换,导致线程里的指令没有执行完就切换执行其它线程了,下面举一个例子 static int count = 0;pu ...

最新文章

  1. 超级实用的linux 下shell快捷键汇总
  2. 深度学总结:weight_initialization
  3. hdu-2844 Coins (混合背包+二进制优化)
  4. hadoop-09-安装资源上传
  5. 陶哲轩对数学学习的一些建议
  6. VirtualBox host-only 外网
  7. SAP WM Stock Removal Strategy - StringentFIFO 在仓库号级别下的先进先出
  8. vb.net教程 3-1 窗体编程基础 1
  9. leetcode刷题(第739题)——每日温度
  10. java贪吃蛇保存,Java 实现贪吃蛇游戏的示例
  11. C# 清除cookies
  12. 洛谷.3733.[HAOI2017]八纵八横(线性基 线段树分治 bitset)
  13. 北京54坐标与西安80坐标相互转换的两种方法
  14. 杨过为什么不喜欢郭芙、郭襄、程瑛……
  15. 重装电脑系统(参考)
  16. 一些文学常识。。。。。。
  17. 2021年牛宝宝起名取名,惊艳有诗意的三字女孩名
  18. babel7 + corejs3升级
  19. 低学历逆袭难,真的只是不够努力吗?
  20. 实验二 数据类型、运算符与表达式→ 张玉生《C语言程序设计实训教程》双色版 配套实验书答案 (纯手打, 仅供参考)

热门文章

  1. 一道CTF----BUUCTF---练习场---Havefun
  2. 第1节 IMPALA:4、5、linux磁盘的挂载和上传压缩包并解压
  3. 干掉搜狗输入法云代理SogouCloud.exe
  4. [trouble shoot]atol和atoll
  5. 深入React事件系统(React点击空白部分隐藏弹出层;React阻止事件冒泡失效)
  6. 04/28/2010 类,对象,变量
  7. lock锁和monitor.enter锁
  8. 怎么让datagridview中的某一行不可编辑
  9. 桌面消息提醒_对win7的支持已近尾声,如何关闭不断弹出的提醒通知
  10. java中servletcontext_java中获取ServletContext常见方法