注意事项:
1、不要在请求方法里另起一个子线程调用该方法;
2、在请求周期中,尽可能不要传递Request实例给多线程使用,因为子线程可能在Request生命周期结束销毁后再使用Request时获取不了参数,否则必须同步线程 让其在生命周期结束前调用;
在Spring Boot中,如果我们要获取当前Request实例,可以通过以下这个方法获取。
在Spring Boot中,如果我们要获取当前Request
实例,可以通过以下这个方法获取。
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
使用这种方法获取的时候需要注意使用多线程会出现些状况,例如一个请求过来后,请求达到Service
方法,然后Service
方法里另起一个线程启动,在该线程run
方法里面想要通过以上方法可能获取不到Request
实例。
且看RequestContextHolder
内部分源码:
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
.....
/**
* Bind the given RequestAttributes to the current thread.
* @param attributes the RequestAttributes to expose,
* or {@code null} to reset the thread-bound context
* @param inheritable whether to expose the RequestAttributes as inheritable
* for child threads (using an {@link InheritableThreadLocal})
*/
public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
if (attributes == null) {
resetRequestAttributes();
}
else {
if (inheritable) {
inheritableRequestAttributesHolder.set(attributes);
requestAttributesHolder.remove();
}
else {
requestAttributesHolder.set(attributes);
inheritableRequestAttributesHolder.remove();
}
}
}
/**
* Return the RequestAttributes currently bound to the thread.
* @return the RequestAttributes currently bound to the thread,
* or {@code null} if none bound
*/
@Nullable
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = requestAttributesHolder.get();
if (attributes == null) {
attributes = inheritableRequestAttributesHolder.get();
}
return attributes;
}
可看到之所以能通过静态方法getRequestAttributes
获取Request
实例,是因为ThreadLocal
获取。一个请求到达容器后,Spring会把该请求Request实例通过setRequestAttributes
方法 把Request
实例放入该请求线程内ThreadLocalMap
中,然后就可以通过静态方法取到。原理就是ThreadLocal
,但ThreadLocal
不能让子线程继承ThreadLocalMap
信息,可以使用InherbritableThreadLocal
实现子线程信息传递。
但Spring Boot 默认使用ThreadLocal
把Request
设置进请求线程中,这样如果在请求方法里面另起一个子线程然后再通过getRequestAttributes
方法获取,是获取不到的。
所以要在能让子线程获取到,就可以使用InherbritableThreadLocal,public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable)
中 inheritable
就代表是否可以继承,这里设置成true即可。