javascript
Spring MVC 成员变量 request 线程安全问题的讨论
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 线程安全问题的讨论的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: redux 存值 及 取值 的操作
- 下一篇: 联想宣布参展 MWC 2023:预计发布