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

之前做项目的时候,在controller中多个方法需要用到request和session获取用户相关值,为了方便写了个BaseController所有controller基础它,在BaseController中Autowired注解request和httpsession,这样子,不需要在各个接口单独加上request入参。这样子的设计在开发阶段和测试阶段都用了有一段时间,一直没有问题。最近偶然在国内技术博客上看到说这样子会有线程安全问题,心想是不是之前的测试没做好,遂马上着手重新进行了单元测试,再次证明没有发现线程安全问题。


针对spring下接口共用公共变量request会不会产生线程安全问题进行测试显示,同一个Controller中hashcode是相同的(说明了request并不会在每次请求中重新生成新的对象注入),但却并不会产生线程安全问题,每次请求获取到的参数都是新的是正确的。实际上,在框架初始IOC的时候是创建了一个Request对象的代理类,从而完成了初始注入,代理类负责从ThreadLocal中获取真正的Request对象并调用相应的方法,每次调用代理request的方法都相当于调用了该次请求真正的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());
	
	@Autowired
	TokenConstitutor tokenConstitutor;
	@Autowired(required=false)
	HttpServletRequest request;
	
	public String getUserId(){
		return (String) tokenConstitutor.getToken(request).getAttribute("userId");
	}
	
	/**
	 * 初始化数据绑定
	 * 1. 将所有传递进来的String进行HTML编码,防止XSS攻击
	 * 2. 将字段中Date类型转换为String类型
	 */
	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		
		// String类型转换,将所有传递进来的String进行HTML编码,防止XSS攻击
		binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				
//				setValue(text == null ? null : StringEscapeUtils.escapeHtml4(text.trim()));
				setValue(text == null ? null : StringEscapeUtil.escapeHtml4(text.trim()));
			}
			@Override
			public String getAsText() {
				Object value = getValue();
				return value != null ? value.toString() : "";
			}
		});
		// Date 类型转换
		binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				long timestamp = Long.parseLong(text);
				setValue(new Date(timestamp));
			}
			
			@Override
			public String getAsText() {
				Date value = (Date) getValue();
				return value.getTime()+"";
			}
		});
	}
	
}


继承:

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

调用getUserID():

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



猜你喜欢

转载自blog.csdn.net/Vincent_Field/article/details/76192388