springmvc在controller成员变量中注入request(@Autowired),会有线程安全问题。
查阅了很多文章,都说request是绑定在ThreadLocal里面的,在注入的时候是一个代理对象,实际的request对象是注入到ThreadLocal中的。
但是在文件上传业务场景中出现了bug
一、 业务场景
文件上传业务,当文件上传接口处理时间较长,在强制转换(MultipartHttpServletRequest) request 之前,又有新的请求到达这个Controller, 然后在执行到强制转换的时候,会报错RequestFacade cannot be cast to org.springframework.web.multipart.MultipartHttpServletRequest 。
二、业务代码
@Autowired
private HttpServletRequest httpServletRequest;
/**
* 模型轻量化转换
*
* @return ResultJson
*/
@RequestMapping("/uploadToBimServer")
public JsonResult uploadToBimServer(@RequestParam Map<String, Object> reqMap, HttpServletRequest httpServletRequest) {
JRTransaction trans = JRTransaction.BeginTransaction();
String userId = this.getUserAuth().getProjectUserId();
Integer prjId = this.getUserAuth().getPrjId();
Integer entId = this.getUserAuth().getEntId();
String orgId = this.getUserAuth().getProjectUserObj().getXss01OrgId();
Xss12ProjectInfo prjObj = this.getUserAuth().getPrjObj();
JsonResult result = new JsonResult();
try {
if (!(request instanceof MultipartHttpServletRequest)) {// 判断流中是否有文件信息
log.error("上传文件数据为空");
return result.buildErrorResult("请选择文件");
}
String name = request.getClass().getName();
System.out.println("name1: " + name);
String uploadCancelId = (String)reqMap.get("uploadCancelId");
// Assert.isTrue(StringUtils.isNotNull(uploadCancelId),"缺少必要参数!");
redisUtil.set(RedisKeyConstant.CANCEL_UPLOAD_FILE_FLAG + uploadCancelId,"1",RedisKeyConstant.CANCEL_UPLOAD_FILE_FLAG_TIMEOUT);
Thread.sleep(5000);
String name2 = request.getClass().getName();
System.out.println("name2: " + name2);
String name3 = httpServletRequest.getClass().getName();
System.out.println("name3: " + name3);
// MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
// MultipartHttpServletRequest multipartRequest = resolver.resolveMultipart(request);
// String name3 = multipartRequest.getClass().getName();
// System.out.println("name3: " + name3);
// 开始处理上传文件
result = _Xfd02ProjectDocService.uploadToBimServer(prjId, reqMap, (MultipartHttpServletRequest) request, entId,
orgId, userId, prjObj, "bim", BIM_SERVER_TRANSFER_PATH,uploadCancelId, trans);
trans.commit();
return result;
} catch (Exception e) {
trans.rollback();
this.printErrorLog(e.getMessage());
throw new ServiceException(e.getMessage());
}
}
/**
* 测试请求
*
* @return ResultJson
*/
@RequestMapping("/test")
public String test(@RequestParam Map<String, Object> reqMap) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "测试";
}
三、实现场景
当调用/uploadToBimServer请求后,立即调用/test请求。
四、原因
暂未知,需要研究底层源码
五、避免方法
在写文件上传接口时,不要使用成员变量注入的request
本文探讨了在Spring MVC控制器中通过`@Autowired`注入`HttpServletRequest`导致的线程安全问题。在一个文件上传的业务场景中,由于请求处理时间长,当并发请求到达时,可能导致请求对象混淆,引发类型转换错误。问题出现在请求被绑定到ThreadLocal中,但在并发情况下未正确处理。为避免此类问题,建议在编写文件上传接口时,不使用成员变量注入的请求,而是直接在方法参数中传递`HttpServletRequest`。
4583

被折叠的 条评论
为什么被折叠?



