SpringMVC 在controller层中注入request(不会产生线程安全问题)

来源:互联网 发布:电脑弹古筝软件 编辑:程序博客网 时间:2024/06/06 01:31

之前做项目的时候,在controller中多个方法需要用到request和session获取用户相关值,为了方便写了个BaseController所有controller基础它,在BaseController中Autowired注解request和httpsession,这样子,需要用到request的方法直接调用request就能使用。这样子的开发阶段和测试阶段都用了有一段时间,一直没有问题。最近突然想到spring bean的单例属性,想到之前的做法可能会产生线程安全问题,赶紧打开程序测试了好几遍,并没有发现线程安全问题,对于这个结果我也是觉得很有意思。上网看了很多文章,都说这样子会有线程安全问题,execuse me,会有线程安全问题的话,那我之前的一大堆测试的结果是怎么回事,难道是我测的不够彻底?心里是不相信的,终于在一堆文章中找到一篇与我观点一致的,遂点进去看了看,看完之后,对于结果很是满意基本跟我的猜想一致,关于不会有线程安全问题的原因,就在这篇文章的评论里。点击打开链接 


下面是评论中的答案(截图发布不了,只能直接拷贝了):

根据楼主测试:同一个Controller中hashcode是相同的,不同Controller中hashcode是不同的。我猜测:HttpServletRequest本身是个接口,在框架初始IOC的时候使用了HttpServletRequest一个实现类创建一个Request对象,从而完成了初始注入(这个时候hashcode已经固定了),而这个实现类应该是基于类似ThreadLocal的手段重写了所有方法,像这样:getParame() ---实际实现为-->getParame(){returnthreadLocal.get("CURRENT_REQUEST").getParam()}。即:获得当前线程真正的Request对象并调用方法,Controller里的Request对象仅仅是一个壳而已,底层依旧产生了线程独立的(拥有独立hashcode的)Request对象,只是被包在这壳里。


所以,我要告诉大家的是,在controller中@Autowire注入request,并不会产生线程安全问题,可以放心的使用。

这是我再controller中注入使用的代码:

基类BaseController(这里的TokenConstitutor原来我是用的spring的HttpSession,后来几个项目组讨论决定统一使用token,才自己做了token获取userId的封装,不需要太在意):

import java.beans.PropertyEditorSupport;import java.util.Date;import javax.servlet.http.HttpServletRequest;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.WebDataBinder;import org.springframework.web.bind.annotation.InitBinder;import com.wanships.framework.TokenConstitutor;import com.wanships.utils.StringEscapeUtil;public class BaseController {Logger logger = LoggerFactory.getLogger(getClass());@AutowiredTokenConstitutor tokenConstitutor;@Autowired(required=false)HttpServletRequest request;public String getUserId(){return (String) tokenConstitutor.getToken(request).getAttribute("userId");}/** * 初始化数据绑定 * 1. 将所有传递进来的String进行HTML编码,防止XSS攻击 * 2. 将字段中Date类型转换为String类型 */@InitBinderprotected void initBinder(WebDataBinder binder) {// String类型转换,将所有传递进来的String进行HTML编码,防止XSS攻击binder.registerCustomEditor(String.class, new PropertyEditorSupport() {@Overridepublic void setAsText(String text) {//setValue(text == null ? null : StringEscapeUtils.escapeHtml4(text.trim()));setValue(text == null ? null : StringEscapeUtil.escapeHtml4(text.trim()));}@Overridepublic String getAsText() {Object value = getValue();return value != null ? value.toString() : "";}});// Date 类型转换binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {@Overridepublic void setAsText(String text) {long timestamp = Long.parseLong(text);setValue(new Date(timestamp));}@Overridepublic String getAsText() {Date value = (Date) getValue();return value.getTime()+"";}});}}


继承:

@Controller@RequestMapping(value = { "${path.prefix.webctrl}/area" })public class WebAreaMarkController extends BaseController{...

调用getUserID():

@RequestMapping("deleteMark")@ResponseBodypublic BaseOut deleteMark(@RequestParam Integer id) {String userId = getUserId();return ConsMsgUtil.getBaseRtn(amService.deleteMark(userId, id));}



阅读全文
0 0
原创粉丝点击