异步调用该代码时:
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
try {
//...获取导入的文件list
asyncTask.saveImport(tblYyCalculateService,list);
log.info("消耗时间" + (System.currentTimeMillis() - start) + "毫秒");
return Result.ok("文件导入成功!数据行数:" + list.size());
} catch (Exception e) {
log.error(e.getMessage(), e);
return Result.error(e.getMessage());
}
}
@Async
public void saveImport(IService service, List<TblYyCalculate> list) {
service.saveBatch(list);
MessageDTO messageDTO = new MessageDTO();
messageDTO.setFromUser("admin");
//openfeign调用执行成功通知
sysBaseAPI.sendSysAnnouncement(messageDTO);
}
sysBaseAPI会通过OpenFeign请求其他模块的接口,这时会通过FeignConfig中的拦截器获取Header中的Token:
return (requestTemplate) -> {
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
String token;
String queryLine;
String signUrls;
if (null != attributes) {
HttpServletRequest request = attributes.getRequest();
log.debug("Feign request: {}", request.getRequestURI());
token = request.getHeader("X-Access-Token");
if (token == null || "".equals(token)) {
token = request.getParameter("token");
if (StringUtils.isEmpty(token)) {
token = UserTokenContext.getToken();
}
}
log.info("Feign Login Request token: {}", token);
requestTemplate.header("X-Access-Token", new String[]{token});
queryLine = request.getHeader("X-Tenant-Id");
if (queryLine == null || "".equals(queryLine)) {
queryLine = request.getParameter("X-Tenant-Id");
}
log.info("Feign Login Request tenantId: {}", queryLine);
requestTemplate.header("X-Tenant-Id", new String[]{queryLine});
} else {
signUrls = UserTokenContext.getToken();
log.info("Feign no Login token: {}", signUrls);
requestTemplate.header("X-Access-Token", new String[]{signUrls});
token = TenantContext.getTenant();
log.info("Feign no Login tenantId: {}", token);
requestTemplate.header("X-Tenant-Id", new String[]{token});
}
此时由于多线程上下文切换导致RequestContextHolder中的ThreadLocal无法获取原先线程中的request,必然会导致token无效,请求失败。
那么,按照网上大多数的方法,直接将ServletRequestAttributes 通过参数传递给异步线程,再手动set到RequestContextHolder中,问题就可以解决了吗?
测试发现,request在主线程结束时会由于某些逻辑导致销毁,此时异步线程如果后执行,那么request的引用虽然还在,但内部的所有对象都为null。当然如果让主线程睡眠几秒,那么还可以让异步线程赶在主线程的request销毁前成功获取内部的对象。
为什么会导致request内部的对象被销毁,但request却还在,目前尚不清楚,肯请评论区大佬解惑。