HttpServletRequest被注入到静态的引用中是否有线程安全问题呢?

HttpServletRequest被注入到静态的引用中是否有线程安全问题呢?

先给出结论:我做的结果是不会。
问题引入:
在做系统添加日志的时候,每次新写Controller的时候都需要引入日志类的Service的对象,实在过于麻烦,于是写了一个日志工具类,将添加日志的方法写为一个静态的方法。


@Component
public class CommonUtil {

	private CommonUtil(){}

    private static LogService logService;
    
    @Resource
    public void setLogService(LogService logService) {
    	CommonUtil.logService = logService;
    }
    public static  void saveLog(HttpServletRequest request,Log log){
    	//通过logService对象进行保存
    }
}

对于静态方法中注入对象,这个已经非常常见了,不足为奇,方式也是很简单,写一个set方法,set方法上写@Resource,这样Spring在启动的时候会调用这个方法给声明的属性注入对象。(这个没深究,大概意思是这样,应该是在启动的时候注入)

然后就觉得,还需要传递request,这个东西也是也可以注入,就想到了下面这样的代码


@Component
public class CommonUtil {

	private CommonUtil(){}

    private static LogService logService;
    
    @Resource
    public void setLogService(LogService logService) {
    	CommonUtil.logService = logService;
    }
    
	private static HttpServletRequest httpServletRequest;

	@Resource
	public void setHttpServletRequest(HttpServletRequest httpServletRequest) {
		System.out.println(httpServletRequest);
		CommonUtil.httpServletRequest = httpServletRequest;
	}

    public static  void saveLog(Log log){
    	//通过logService对象进行保存
    }
}

想法很奇怪,做法也很奇怪,但是测试了一下,可以用,我就没管了,知道我同事过来跟我说这个可能有线程安全问题,(毕竟需要从request里设置用户访问时候的ip,如果有线程安全问题的话,日志记录的就可能是A在ip1访问的记录,记录成了A在ip2的记录,问题还是比较大的,仔细想了一下。也确实会有安全问题)

然后百度了一下,大多数人都不是注入到静态的声明上,如下代码

	@Resource
	private HttpServletRequest httpServletRequest;

然后看了一下,先说结论:这样不会有线程安全问题。

解释有很多文章在讲,这里推荐一个讲的比较详细的,点下方进行查看,这里不做过多赘述,我们主要说被注入到静态引用中是否会有线程安全问题,

点击传送门

因为是在工作中,最快解决问题才是最主要的,我第一反应就是把这个request用参数传递的方式传递过来,无非就是多传递一个参数而已,对我来说只是把所有调用这个方法的地方改一遍而已,而且现在Ctrl+H可以直接替换了,不是啥大问题。但是这个问题困扰着我,所以写了下面的代码进行测试


/**
 * TestController
 */
@RestController
public class TestController {
	
	
	
	@GetMapping("/test")
	public String test(HttpServletRequest request) {
		CommonUtil.getParam(request);
		return "success";
	}

}


/**
 * ClassName:CommonUtil
 */
@Component
public class CommonUtil {
	
	
	private CommonUtil() {}
	
	
	private static HttpServletRequest httpServletRequest;

	@Resource
	public void setHttpServletRequest(HttpServletRequest httpServletRequest) {
		System.out.println(httpServletRequest);
		CommonUtil.httpServletRequest = httpServletRequest;
	}
	
	public static void getParam(HttpServletRequest request)  {
		String v = request.getParameter("v");
		if(v.equals("1")) {
			try {
				Thread.sleep(10000);
			}catch(Exception e) {
				
			}
		}
		String z = httpServletRequest.getParameter("z");
		System.out.println(z);
	}

}

在启动的时候发现了一个输出:

注意下红色框的内容

红色框中的内容,代表了系统启动的时候会设置上静态的HttpServletRequest对象

这是两个浏览器

开始测试:
根据代码逻辑,在访问参数v=1时,进入到CommonUtil的getParam的方法内,会睡10S,在此10S内,访问参数v=2时,不会进入睡眠代码
第一步:在浏览器1中访问http://127.0.0.1:99/test?v=1&z=10
在执行睡眠代码的10S内执行第二步
第二步:在浏览器2中访问http://127.0.0.1:99/test?z=200&v=2
等待两个浏览器页面都显示了success之后,控制台的输出内容为:

输出结果

输出结果表示了,静态HttpServletRequest不会有线程安全的问题。
至于为啥,我没深究,上面的传送门有代码跟踪,有大神懂的话,可以评论一下。

虽然证明了不会有线程安全的问题,但是不建议这么写到生产环境。

第一可读性非常的差

第二,我已经被老大骂过一遍了,事实证明这么写会影响心情


版权声明:本文为weixin_47721508原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。