为了账号安全,请及时绑定邮箱和手机立即绑定

Springmvc中在controller注入request会有线程安全问题吗

Springmvc中在controller注入request会有线程安全问题吗

RISEBY 2019-03-13 13:14:33
@Controllerpublic class AController{    @Autowire    HttpServletRequest request;        @RequestMapping("/test")    public Result test(){        System.out.println(request.toString());        request.getHeader("uid");    }}例如上述代码,我使用Autowire注入request后,直接在controller的方法中使用request,由于controller默认是单例的,我在想是否会有线程安全问题。因为我输出了request的hashcode发现每次请求hashcode都是一样的。那么后面的request是否会覆盖当前request导致信息失真?·····························补充··························1、我想在controller的每个方法里都使用HttpServletRequest,那么每次在方法里都要声明我觉得就比较麻烦了?不知道大家怎么解决这个问题?2、我这样写的原因是,我想通过继承一个父类的方式,让request作为父类的一个成员变量,这样可以在方法里直接使用。3、我通过楼下叉叉哥的方式(之前就是这样写的)public Result test(HttpServletRequest request){    System.out.println(request.toString());}同样不断访问,或者用不同客户端访问。发现打印出来的每个请求的request的hashcode居然也是相同的,为什么?
查看完整描述

11 回答

?
翻阅古今

TA贡献1780条经验 获得超5个赞

这么写是线程安全的,原因如下:

  1. 在controller中注入的request是jdk动态代理对象,ObjectFactoryDelegatingInvocationHandler的实例.当我们调用成员域request的方法的时候其实是调用了objectFactory的getObject()对象的相关方法.这里的objectFactory是RequestObjectFactory.

  2. RequestObjectFactory的getObject其实是从RequestContextHolder的threadlocal中去取值的.

  3. 请求刚进入springmvc的dispatcherServlet的时候会把request相关对象设置到RequestContextHolder的threadlocal中去.

参考: 在SpringMVC Controller中注入Request成员域


查看完整回答
1 反对 回复 2019-04-18
?
繁花如伊

TA贡献2012条经验 获得超12个赞

谢谢大家的回答
经过我的测试和探索。
得出的结论是
使用@autowire注入HttpServletRequest是线程安全的。
具体验证过程我写了篇blog
有兴趣的可以看看,如果有什么不对的地方,请大家指出来。
再次感谢楼上热心回答我问题的程序员们

blog地址戳我

ps:以上讨论的都是Controller为单例模式下的

···


查看完整回答
反对 回复 2019-04-18
?
慕后森

TA贡献1802条经验 获得超5个赞

首先,原则上 Request 不可以定义为 Controller 的成员,因为二者的生命周期实在是完全脱节的,这样子弄只会造成 Controller 无法调用到正确的 Request 对象。

其次 @Autowire 是一次性赋值的,而 Request 对象有无数多个,所以你这样写的话,Spring 也会不知该如何是好。因为应用启动的时候根本没有 Request 对象,所以这样应该会导致启动失败。


查看完整回答
反对 回复 2019-04-18
?
ibeautiful

TA贡献1993条经验 获得超5个赞

第一次见过这种写法,为什么不这样写?


@Controller

public class AController{

    

    @RequestMapping("/test")

    public Result test(HttpServletRequest request){

        System.out.println(request.toString());

        request.getHeader("uid");

    }

}

实际上在大部分情况下,request都不用作为参数传过来的,比如你想拿到请求头中的uid,可以这样写:


@Controller

public class AController{

    

    @RequestMapping("/test")

    public Result test(@RequestHeader("uid") String uid) {

        System.out.println(uid); // 相当于request.getHeader("uid")

    }

}

20180613补充2年前的回答:

上面的回答没有直接回答题主提出的问题,所以算是不正确的回答。下面补充回答一下:


题主给的这种方式是线程安全的!!这种做法是完全没问题的。


@Autowire

HttpServletRequest request;

首先,Controller的确是单实例的,如果请求并发高会不会导致多个线程用到同一个请求的HttpServletRequest对象?答案是不会的。


实际上Autowire进来的并不是原始的HttpServletRequest对象,而是HttpServletRequest的一个代理类。实际上它会通过


((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()

这样的方式来获取真正的Request对象。而RequestContextHolder是通过ThreadLocal来实现的,可以保证每个线程获取得到的Request对象一定是当前请求的Request对象,从而保证线程安全。


查看完整回答
反对 回复 2019-04-18
?
哆啦的时光机

TA贡献1779条经验 获得超6个赞

会有的。在Servlet里,都是把属性参数写在方法里,这样就不会被共享


查看完整回答
反对 回复 2019-04-18
?
小唯快跑啊

TA贡献1863条经验 获得超2个赞

任何类实例变量都有潜在的线程安全的风险,代码需要确保该实例变量在多线程下没问题,或者确保该类同时只有一个线程访问。
你这个例子多线程的问题是必然的,你还是根据Spring的教程按照标准的写法写,先把事情搞对了,理解了,然后再搞复杂的。

查看完整回答
反对 回复 2019-04-18
?
杨魅力

TA贡献1811条经验 获得超6个赞

httpRequestServlet本来就不是类属性,而是线程变量,所以我认为最好的使用方式是用RequestContextHolder. currentRequestAttributes()来取。

查看完整回答
反对 回复 2019-04-18
  • 11 回答
  • 0 关注
  • 1162 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信