由于项目使用的是 mybatis-plus 来自动帮我填充通用字段,而这些通用字段都是从 request 里面取 token 然后解析得来的。
所以我使用 异步的时候,我也需要使用主线程的 request。
最开始我是在主线程中用 RequestContextHolder.getRequestAttributes() 拿到 RequestAttributes ,然在子线程中在 set 进去。
我以为这样就完美解决了,后面发现在异步线程中还是无法使用 request。
通过了解 request 的机制。request 在主线程结束之后,会进行释放。而我上面的操作只是传递了 RequestAttributes ,而 request 还是使用的同一个。
所以我们需要复制一个 request 。但是 request 怎么复制。
request 是不好复制的,但是我们可以采用继承的方式。
首先继承 HttpServletRequestWrapper ,然后我们可以保存 request 的一些需要的值。然后重写一下 我们需要使用的一些 request 的方法,从我们自己保存的值去拿。然后我们只需要在子线程中使用我们自己的 request 就好了。
public class HttpRequestCopy extends HttpServletRequestWrapper {
private Map<String,String> header;
private Map<String,Object> attr;
public HttpRequestCopy(HttpServletRequest request) {
super(request);
header = new HashMap<>();
attr = new HashMap<>();
Enumeration<String> headers = request.getHeaderNames();
if (headers != null) {
while (headers.hasMoreElements()) {
String header = (String) headers.nextElement();
this.header.put(header, request.getHeader(header));
}
}
}
@Override
public String getHeader(String name) {
return header.get(name);
}
}
其实 request 是不建议在异步中使用的,所有 request 还提供了一个 startAsync() 方法,这个方法可以保证 request 不被回收。虽然可以异步,但是这个方法也会导致这个请求不会随着主线程的结束被响应。而是等程序调用 compete() 方法后才会释放 request 并响应请求。