搜索

首页  >  问答  >  正文

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

@Controller
public class AController{

    @Autowire
    HttpServletRequest request;
    
    @RequestMapping("/test")
    public Result test(){
        System.out.println(request.toString());
        request.getHeader("uid");
    }
}

例如上述代码,
我使用Autowire注入request后,直接在controller的方法中使用request
由于controller默认是单例的,我在想是否会有线程安全问题。
因为我输出了requesthashcode发现每次请求hashcode都是一样的。
那么后面的request是否会覆盖当前request导致信息失真?

·····························补充··························

1、我想在controller的每个方法里都使用HttpServletRequest,那么每次在方法里都要声明我觉得就比较麻烦了?不知道大家怎么解决这个问题?
2、我这样写的原因是,我想通过继承一个父类的方式,让request作为父类的一个成员变量,这样可以在方法里直接使用。
3、我通过楼下叉叉哥的方式(之前就是这样写的)

public Result test(HttpServletRequest request){

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

同样不断访问,或者用不同客户端访问。发现打印出来的每个请求的request的hashcode居然也是相同的,为什么?

高洛峰高洛峰2888 天前572

全部回复(10)我来回复

  • 巴扎黑

    巴扎黑2017-04-17 17:59:04

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

    blog地址戳我

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

    ···

    回复
    0
  • 大家讲道理

    大家讲道理2017-04-17 17:59:04

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

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

    回复
    0
  • PHP中文网

    PHP中文网2017-04-17 17:59:04

    额,我献丑了,光顾着解决次要的request每次相同的问题了,忘了主题了。
    @ModelAttribute确实会有线程安全问题!
    ... request每次相同的问题了,忘了主题了。
    @ModelAttribute确实会有线程安全问题!
    ...

    在要使用对象属性,的情况下还处理线程安全的话,有一种方法很简单,但是特别糙,直接使用@Scope("prototype")让Spring MVC每次请求都生成一个新的实体类...
    但Spring MVC为什么默认要单例?自然是因为单例的性能和开销的优点,
    而写@Scope("prototype")

    在要使用对象属性,的情况下还处理线程安全的话,有一种方法很简单,但是特别糙,直接使用@Scope("prototype")让Spring MVC每次请求都生成一个新的实体类...
    但Spring MVC为什么默认要单例?自然是因为单例的性能和开销的优点,
    而写@Scope("prototype")之后就意味着放弃单例的优点。

    所以这确实是一种方式,但是并不好,给各位献丑了,抱歉。🎜
    @Controller
    // @Scope("prototype")
    public class AController{
    
        //@Autowired //如果采用非单例模式的话,用autowried也一样。
        protected HttpServletRequest request;
        protected HttpServletResponse response;
        
        @ModelAttribute
        public void bindRequestObject(HttpServletRequest request, HttpServletResponse response) {
            this.request = request;
            this.response = response;
        }
        
        @RequestMapping("/test")
        public Result test(){
            System.out.println(request.toString());
            request.getHeader("uid");
        }
    }

    回复
    0
  • 迷茫

    迷茫2017-04-17 17:59:04

    1、Autowire注入request后,使用实例变量会有安全问题
    2、会覆盖request
    3、hashcode相同还是一个对象

    觉的方法中写参数麻烦,这样可以:

    @Controller
    public class AControllre extends AbstractController {
    
        @RequestMapping("/test")
        public String test(){
            //使用
            String name = getRequest().getParameter("username");
            return "";
        }
    }
    
    class AbstractController {
        protected HttpServletRequest getRequest() {
            return ((ServletRequestAttributes) 
                    RequestContextHolder.getRequestAttributes()).getRequest();
        }
        protected HttpServletResponse getResponse() {
            return new ServletWebRequest(((ServletRequestAttributes) 
                    RequestContextHolder.getRequestAttributes()).getRequest()).getResponse();
        }
    }

    回复
    0
  • PHPz

    PHPz2017-04-17 17:59:04

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

    @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")
        }
    }

    回复
    0
  • 阿神

    阿神2017-04-17 17:59:04

    你那样写肯定是会发生线程安全问题的,因为 spring 的每个 controller 默认都是单例的,所以你的 request 会被其他线程给共享,所以建议你和 @叉叉哥 写法一样。

    回复
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-17 17:59:04

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

    回复
    0
  • PHP中文网

    PHP中文网2017-04-17 17:59:04

    你这样写肯定会有线程安全问题,因为controller默认是单例的,说明多次请求会共享一个HttpServletRequest对象,加上@Scope("prototype")就没有问题了

    回复
    0
  • PHP中文网

    PHP中文网2017-04-17 17:59:04

    会有线程安全问题的,建议你看看这篇文章http://www.xuebuyuan.com/1628190.html

    回复
    0
  • 巴扎黑

    巴扎黑2017-04-17 17:59:04

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

    回复
    0
  • 取消回复