如何在spring中获取request对象获取数据的方法

 

使用@ModelAttribute 注解 每次执行都会走这个注解下面的方法


  • 通过注解获取(很简单推荐):
  • 在web.xml中配置一个监听:
  • 直接在参数中传递(此方法不好用,不建议使用):

在使用Spring MVC开发Web系统时经常需要在處理请求时使用request对象获取数据的方法,比如获取客户端ip地址、请求的url、header中的属性(如cookie、授权信息)、body中的数据等由于在Spring MVC中,处理请求的Controller、Service等对象都是单例的因此获取request对象获取数据的方法时最需要注意的问题,便是request对象获取数据的方法是否是线程安全的:当有大量并发请求时能否保证不同请求/线程中使用不同的request对象获取数据的方法。

这里还有一个问题需要注意:前面所说的“在处理请求时”使用request对象获取数据的方法究竟是在哪里使用呢?考虑到获取request对象获取数据的方法的方法有微小的不同大体可以分为两类:

  1. 在非Bean中使用request对象获取数據的方法:如普通的Java对象的方法中使用,或在类的静态方法中使用

最后,获取request对象获取数据的方法的方法与Spring及MVC的版本也有关系;本文基於Spring4进行讨论且所做的实验都是使用4.1.1版本。

这种方法实现最简单直接上Controller代码:

// 模拟程序执行了一段时间

该方法实现的原理是,在Controller方法开始处理请求时Spring会将request对象获取数据的方法赋值到方法参数中。除了request对象获取数据的方法可以通过这种方法获取的参数还有很多,具体可鉯参见:

Controller中获取request对象获取数据的方法后如果要在其他方法中(如service方法、工具类方法等)使用request对象获取数据的方法,需要在调用这些方法時将request对象获取数据的方法作为参数传入

分析:此时request对象获取数据的方法是方法参数,相当于局部变量毫无疑问是线程安全的。

这种方法的主要缺点是request对象获取数据的方法写起来冗余太多主要体现在两点:

  1. 如果多个controller方法中都需要request对象获取数据的方法,那么在每个方法中嘟需要添加一遍request参数
  2. request对象获取数据的方法的获取只能从controller开始如果使用request对象获取数据的方法的地方在函数调用层级比较深的地方,那么整個调用链上的所有方法都需要添加request参数

实际上在整个请求处理的过程中,request对象获取数据的方法是贯穿始终的;也就是说除了定时器等特殊情况,request对象获取数据的方法相当于线程内部的一个全局变量而该方法,相当于将这个全局变量传来传去。

//模拟程序执行了一段时間

使用这种方式当Bean(本例的TestController)初始化时,Spring并没有注入一个request对象获取数据的方法而是注入了一个代理(proxy);当Bean中需要使用request对象获取数据嘚方法时,通过该代理获取request对象获取数据的方法

下面通过具体的代码对这一实现进行说明。

在上述代码中加入断点查看request对象获取数据嘚方法的属性,如下图所示:

// ……省略无关代码 // 此处省略不相关逻辑…………

通过这段代码可以看出生成的RequestAttributes对象是线程局部变量(ThreadLocal),洇此request对象获取数据的方法也是线程局部变量;这就保证了request对象获取数据的方法的线程安全性

  1. 减少代码冗余:只需要在需要request对象获取数据嘚方法的Bean中注入request对象获取数据的方法,便可以在该Bean的各个方法中使用与方法1相比大大减少了代码冗余。

但是该方法也会存在代码冗余。考虑这样的场景:web系统中有很多controller每个controller中都会使用request对象获取数据的方法(这种场景实际上非常频繁),这时就需要写很多次注入request的代码;如果还需要注入response代码就更繁琐了。下面说明自动注入方法的改进方法并分析其线程安全性及优缺点。

与方法2相比将注入部分代码放入到了基类中。

Controller代码如下;这里列举了BaseController的两个派生类由于此时测试代码会有所不同,因此服务端测试代码没有省略;客户端也需要进荇相应的修改(同时向2个url发送大量并发请求)

// 存储已有参数,用于判断参数value是否重复从而判断线程是否安全 // 模拟程序执行了一段时间 // 模拟程序执行了一段时间

分析:在理解了方法2的线程安全性的基础上,很容易理解方法3是线程安全的:当创建不同的派生类对象时基类Φ的域(这里是注入的request)在不同的派生类对象中会占据不同的内存空间,也就是说将注入request的代码放在基类中对线程安全性没有任何影响;測试结果也证明了这一点

与方法2相比,避免了在不同的Controller中重复注入request;但是考虑到java只允许继承一个基类所以如果Controller需要继承其他类时,该方法便不再好用

无论是方法2和方法3,都只能在Bean中注入request;如果其他方法(如工具类中static方法)需要使用request对象获取数据的方法则需要在调用這些方法时将request参数传递进去。下面介绍的方法4则可以直接在诸如工具类中的static方法中使用request对象获取数据的方法(当然在各种Bean中也可以使用)。

// 模拟程序执行了一段时间

分析:该方法与方法2(自动注入)类似只不过方法2中通过自动注入实现,本方法通过手动方法调用实现洇此本方法也是线程安全的。

优点:可以在非Bean中直接获取缺点:如果使用的地方较多,代码非常繁琐;因此可以与其他方法配合使用

丅面这种方法及其变种(变种:将request和bindRequest放在子类中)在网上经常见到:

// 模拟程序执行了一段时间

综上所述,Controller中加参数(方法1)、自动注入(方法2和方法3)、手动调用(方法4)都是线程安全的都可以用来获取request对象获取数据的方法。如果系统中request对象获取数据的方法使用较少则使用哪种方式均可;如果使用较多,建议使用自动注入(方法2 和方法3)来减少代码冗余如果需要在非Bean中使用request对象获取数据的方法,既可鉯在上层调用时通过参数传入也可以直接在方法中通过手动调用(方法4)获得。

我要回帖

更多关于 request对象获取数据的方法 的文章

 

随机推荐