首页 » 网站推广 » php获取数据request技巧_Spring中获取request的几种方法及其线程安然性分析

php获取数据request技巧_Spring中获取request的几种方法及其线程安然性分析

duote123 2024-11-20 0

扫一扫用手机浏览

文章目录 [+]

本文将先容在Spring MVC开拓的web系统中,获取request工具的几种方法,并谈论其线程安全性。

原创不易,如果以为文章对你有帮助,欢迎点赞、评论。
文章有疏漏之处,欢迎批评示正。

php获取数据request技巧_Spring中获取request的几种方法及其线程安然性分析

欢迎转载,转载请注明原文链接:http://www.cnblogs.com/kismetv/p/8757260.html

php获取数据request技巧_Spring中获取request的几种方法及其线程安然性分析
(图片来自网络侵删)

目录

概述

如何测试线程安全性

方法1:Controller中加参数

方法2:自动注入

方法3:基类中自动注入

方法4:手动调用

方法5:@ModelAttribute方法

总结

概述

在利用Spring MVC开拓Web系统时,常常须要在处理要求时利用request工具,比如获取客户端ip地址、要求的url、header中的属性(如cookie、授权信息)、body中的数据等。
由于在Spring MVC中,处理要求的Controller、Service等工具都是单例的,因此获取request工具时最须要把稳的问题,便是request工具是否是线程安全的:当有大量并发要求时,能否担保不同要求/线程中利用不同的request工具。

这里还有一个问题须要把稳:前面所说的“在处理要求时”利用request工具,究竟是在哪里利用呢?考虑到获取request工具的方法有眇小的不同,大体可以分为两类:

1) 在Spring的Bean中利用request工具:既包括Controller、Service、Repository等MVC的Bean,也包括了Component等普通的Spring Bean。
为了方便解释,后文中Spring中的Bean一律简称为Bean。

2) 在非Bean中利用request工具:如普通的Java工具的方法中利用,或在类的静态方法中利用。

此外,本文谈论是环绕代表要求的request工具展开的,但所用方法同样适用于response工具、InputStream/Reader、OutputStream/ Writer等;个中InputStream/Reader可以读取要求中的数据,OutputStream/ Writer可以向相应写入数据。

末了,获取request工具的方法与Spring及MVC的版本也有关系;本文基于Spring4进行谈论,且所做的实验都是利用4.1.1版本。

如何测试线程安全性

既然request工具的线程安全问题须要特殊关注,为了便于后面的谈论,下面先解释如何测试request工具是否是线程安全的。

测试的基本思路,是仿照客户端大量并发要求,然后在做事器判断这些要求是否利用了相同的request工具。

判断request工具是否相同,最直不雅观的办法是打印出request工具的地址,如果相同则解释利用了相同的工具。
然而,在险些所有web做事器的实现中,都利用了线程池,这样就导致先后到达的两个要求,可能由同一个线程处理:在前一个要求处理完成后,线程池收回该线程,并将该线程重新分配给了后面的要求。
而在同一线程中,利用的request工具很可能是同一个(地址相同,属性不同)。
因此即便是对付线程安全的方法,不同的要求利用的request工具地址也可能相同。

为了避免这个问题,一种方法是在要求处理过程中使线程休眠几秒,这样可以让每个线程事情的韶光足够长,从而避免同一个线程分配给不同的要求;另一种方法,是利用request的其他属性(如参数、header、body等)作为request是否线程安全的依据,由于即便不同的要求先后利用了同一个线程(request工具地址也相同),只要利用不同的属性分别布局了两次request工具,那么request工具的利用便是线程安全的。
本文利用第二种方法进行测试。

客户端测试代码如下(创建1000个线程分别发送要求):

public class Test { public static void main(String[] args) throws Exception { String prefix = UUID.randomUUID().toString().replaceAll(\"大众-\"大众, \公众\"大众) + \公众::\公众; for (int i = 0; i < 1000; i++) { final String value = prefix + i; new Thread() { @Override public void run() { try { CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet httpGet = new HttpGet(\"大众http://localhost:8080/test?key=\"大众 + value); httpClient.execute(httpGet); httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } }.start(); } }}

做事器中Controller代码如下(暂时省略了获取request工具的代码):

@Controllerpublic class TestController { // 存储已有参数,用于判断参数是否重复,从而判断线程是否安全 public static Set<String> set = new HashSet<>(); @RequestMapping(\"大众/test\"大众) public void test() throws InterruptedException { // …………………………通过某种办法得到了request工具……………………………… // 判断线程安全 String value = request.getParameter(\公众key\公众); if (set.contains(value)) { System.out.println(value + \"大众\t重复涌现,request并发不屈安!
\公众
); } else { System.out.println(value); set.add(value); } // 仿照程序实行了一段韶光 Thread.sleep(1000); }}

如果request工具线程安全,做事器中打印结果如下所示:

如果存在线程安全问题,做事器中打印结果可能如下所示:

如无分外解释,本文后面的代码中将省略掉测试代码。

方法1:Controller中加参数

代码示例

这种方法实现最大略,直接上Controller代码:

@Controllerpublic class TestController { @RequestMapping(\"大众/test\"大众) public void test(HttpServletRequest request) throws InterruptedException { // 仿照程序实行了一段韶光 Thread.sleep(1000); }}

该方法实现的事理是,在Controller方法开始处理要求时,Spring会将request工具赋值到方法参数中。
除了request工具,可以通过这种方法获取的参数还有很多,详细可以拜会:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods

Controller中获取request工具后,如果要在其他方法中(如service方法、工具类方法等)利用request工具,须要在调用这些方法时将request工具作为参数传入。

线程安全性

测试结果:线程安全

剖析:此时request工具是方法参数,相称于局部变量,毫无疑问是线程安全的。

优缺陷

这种方法的紧张缺陷是request工具写起来冗余太多,紧张表示在两点:

1) 如果多个controller方法中都须要request工具,那么在每个方法中都须要添加一遍request参数

2) request工具的获取只能从controller开始,如果利用request工具的地方在函数调用层级比较深的地方,那么全体调用链上的所有方法都须要添加request参数

实际上,在全体要求处理的过程中,request工具是贯穿始终的;也便是说,除了定时器等分外情形,request工具相称于线程内部的一个全局变量。
而该方法,相称于将这个全局变量,传来传去。

方法2:自动注入

代码示例

先上代码:

@Controllerpublic class TestController{ @Autowired private HttpServletRequest request; //自动注入request @RequestMapping(\公众/test\公众) public void test() throws InterruptedException{ //仿照程序实行了一段韶光 Thread.sleep(1000); }}

线程安全性

测试结果:线程安全

剖析:在Spring中,Controller的scope是singleton(单例),也便是说在全体web系统中,只有一个TestController;但是个中注入的request却是线程安全的,缘故原由在于:

利用这种办法,当Bean(本例的TestController)初始化时,Spring并没有注入一个request工具,而是注入了一个代理(proxy);当Bean中须要利用request工具时,通过该代理获取request工具。

下面通过详细的代码对这一实现进行解释。

在上述代码中加入断点,查看request工具的属性,如下图所示:

在图中可以看出,request实际上是一个代理:代理的实现拜会AutowireUtils的内部类ObjectFactoryDelegatingInvocationHandler:

/ Reflective InvocationHandler for lazy access to the current target object. /@SuppressWarnings(\"大众serial\"大众)private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable { private final ObjectFactory<?> objectFactory; public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) { this.objectFactory = objectFactory; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // ……省略无关代码 try { return method.invoke(this.objectFactory.getObject(), args); // 代理实现核心代码 } catch (InvocationTargetException ex) { throw ex.getTargetException(); } }}

也便是说,当我们调用request的方法method时,实际上是调用了由objectFactory.getObject()天生的工具的method方法;objectFactory.getObject()天生的工具才是真正的request工具。

连续不雅观察上图,创造objectFactory的类型为WebApplicationContextUtils的内部类RequestObjectFactory;而RequestObjectFactory代码如下:

/ Factory that exposes the current request object on demand. /@SuppressWarnings(\公众serial\"大众)private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable { @Override public ServletRequest getObject() { return currentRequestAttributes().getRequest(); } @Override public String toString() { return \"大众Current HttpServletRequest\"大众; }}

个中,要得到request工具须要先调用currentRequestAttributes()方法得到RequestAttributes工具,该方法的实现如下:

/ Return the current RequestAttributes instance as ServletRequestAttributes. /private static ServletRequestAttributes currentRequestAttributes() { RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes(); if (!(requestAttr instanceof ServletRequestAttributes)) { throw new IllegalStateException(\公众Current request is not a servlet request\公众); } return (ServletRequestAttributes) requestAttr;}

天生RequestAttributes工具的核心代码在类RequestContextHolder中,个中干系代码如下(省略了该类中的无关代码):

public abstract class RequestContextHolder { public static RequestAttributes currentRequestAttributes() throws IllegalStateException { RequestAttributes attributes = getRequestAttributes(); // 此处省略不干系逻辑………… return attributes; } public static RequestAttributes getRequestAttributes() { RequestAttributes attributes = requestAttributesHolder.get(); if (attributes == null) { attributes = inheritableRequestAttributesHolder.get(); } return attributes; } private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal<RequestAttributes>(\"大众Request attributes\"大众); private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal<RequestAttributes>(\"大众Request context\"大众);}

通过这段代码可以看出,天生的RequestAttributes工具是线程局部变量(ThreadLocal),因此request工具也是线程局部变量;这就担保了request工具的线程安全性。

优缺陷

该方法的紧张优点:

1) 注入不局限于Controller中:在方法1中,只能在Controller中加入request参数。
而对付方法2,不仅可以在Controller中注入,还可以在任何Bean中注入,包括Service、Repository及普通的Bean。

2) 注入的工具不限于request:除了注入request工具,该方法还可以注入其他scope为request或session的工具,如response工具、session工具等;并担保线程安全。

3) 减少代码冗余:只须要在须要request工具的Bean中注入request工具,便可以在该Bean的各个方法中利用,与方法1比较大大减少了代码冗余。

但是,该方法也会存在代码冗余。
考虑这样的场景:web系统中有很多controller,每个controller中都会利用request工具(这种场景实际上非常频繁),这时就须要写很多次注入request的代码;如果还须要注入response,代码就更繁琐了。
下面解释自动注入方法的改进方法,并剖析其线程安全性及优缺陷。

方法3:基类中自动注入

代码示例

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

基类代码:

public class BaseController { @Autowired protected HttpServletRequest request; }

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

@Controllerpublic class TestController extends BaseController { // 存储已有参数,用于判断参数value是否重复,从而判断线程是否安全 public static Set<String> set = new HashSet<>(); @RequestMapping(\"大众/test\公众) public void test() throws InterruptedException { String value = request.getParameter(\"大众key\"大众); // 判断线程安全 if (set.contains(value)) { System.out.println(value + \公众\t重复涌现,request并发不屈安!
\公众
); } else { System.out.println(value); set.add(value); } // 仿照程序实行了一段韶光 Thread.sleep(1000); }}@Controllerpublic class Test2Controller extends BaseController { @RequestMapping(\"大众/test2\"大众) public void test2() throws InterruptedException { String value = request.getParameter(\"大众key\公众); // 判断线程安全(与TestController利用一个set进行判断) if (TestController.set.contains(value)) { System.out.println(value + \"大众\t重复涌现,request并发不屈安!
\公众
); } else { System.out.println(value); TestController.set.add(value); } // 仿照程序实行了一段韶光 Thread.sleep(1000); }}

线程安全性

测试结果:线程安全

剖析:在理解了方法2的线程安全性的根本上,很随意马虎理解方法3是线程安全的:当创建不同的派生类工具时,基类中的域(这里是注入的request)在不同的派生类工具中会霸占不同的内存空间,也便是说将注入request的代码放在基类中对线程安全性没有任何影响;测试结果也证明了这一点。

优缺陷

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

无论是方法2和方法3,都只能在Bean中注入request;如果其他方法(如工具类中static方法)须要利用request工具,则须要在调用这些方法时将request参数通报进去。
下面先容的方法4,则可以直接在诸如工具类中的static方法中利用request工具(当然在各种Bean中也可以利用)。

方法4:手动调用

代码示例

123456789@Controllerpublic class TestController { @RequestMapping(\"大众/test\"大众) public void test() throws InterruptedException { HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); // 仿照程序实行了一段韶光 Thread.sleep(1000); }}

线程安全性

测试结果:线程安全

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

优缺陷

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

方法5:@ModelAttribute方法

代码示例

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

12345678910111213@Controllerpublic class TestController { private HttpServletRequest request; @ModelAttribute public void bindRequest(HttpServletRequest request) { this.request = request; } @RequestMapping(\"大众/test\"大众) public void test() throws InterruptedException { // 仿照程序实行了一段韶光 Thread.sleep(1000); }}

线程安全性

测试结果:线程不屈安

剖析:@ModelAttribute表明用在Controller中润色方法时,其浸染是Controller中的每个@RequestMapping方法实行前,该方法都会实行。
因此在本例中,bindRequest()的浸染是在test()实行前为request工具赋值。
虽然bindRequest()中的参数request本身是线程安全的,但由于TestController是单例的,request作为TestController的一个域,无法担保线程安全。

总结

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

参考文献

https://docs.spring.io/spring/docs/4.1.x/spring-framework-reference/html/beans.html#beans-factory-scopes-other-injection

https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods

https://stackoverflow.com/questions/10541934/spring-aop-and-aspect-thread-safety-for-an-autowired-httpservletrequest-bean

http://www.phpchina.com/portal.php?mod=view&aid=40966

https://stackoverflow.com/questions/22674044/inject-httpservletrequest-into-controller

https://stackoverflow.com/questions/3320674/spring-how-do-i-inject-an-httpservletrequest-into-a-request-scoped-bean

https://my.oschina.net/sluggarddd/blog/678603?fromerr=XhvpvVTi

https://stackoverflow.com/questions/8504258/spring-3-mvc-accessing-httprequest-from-controller

原创不易,如果以为文章对你有帮助,欢迎点赞、评论。
文章有疏漏之处,欢迎批评示正。

欢迎转载,转载请注明原文链接:http://www.cnblogs.com/kismetv/p/8757260.html

标签:

相关文章

CAD之家助力工程师的数字化设计利器

数字化设计已成为现代工程师们必备的技能。在众多数字化设计工具中,CAD之家凭借其强大的功能、便捷的操作和优质的服务,成为了广大工程...

网站推广 2025-04-14 阅读3 评论0

APP推广员好做吗职业前景与挑战并存

APP已成为人们日常生活中不可或缺的一部分。APP推广员应运而生,成为互联网行业的新兴职业。APP推广员好做吗?本文将从职业前景、...

网站推广 2025-04-14 阅读3 评论0

AutodeskAutoCAD助力工程设计的未来之星

工程设计领域也在经历着日新月异的变化。作为全球领先的工程设计软件,Autodesk AutoCAD凭借其卓越的性能和强大的功能,已...

网站推广 2025-04-14 阅读5 评论0