注意事項(xiàng):
- 不要在請(qǐng)求方法里另起一個(gè)子線(xiàn)程調(diào)用該方法宪哩;
- 在請(qǐng)求周期中通惫,盡可能不要傳遞Request實(shí)例給多線(xiàn)程使用翎苫,因?yàn)樽泳€(xiàn)程可能在Request生命周期結(jié)束銷(xiāo)毀后再使用Request時(shí)獲取不了參數(shù)旺聚,否則必須同步線(xiàn)程 讓其在生命周期結(jié)束前調(diào)用沈贝;
在Spring Boot中犬耻,如果我們要獲取當(dāng)前Request實(shí)例踩晶,可以通過(guò)以下這個(gè)方法獲取。
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
使用這種方法獲取的時(shí)候需要注意使用多線(xiàn)程會(huì)出現(xiàn)些狀況枕磁,例如一個(gè)請(qǐng)求過(guò)來(lái)后渡蜻,請(qǐng)求達(dá)到Service方法,然后Service方法里另起一個(gè)線(xiàn)程啟動(dòng)透典,在該線(xiàn)程run方法里面想要通過(guò)以上方法可能獲取不到Request實(shí)例晴楔。
且看RequestContextHolder內(nèi)部分源碼:
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;
}
可看到之所以能通過(guò)靜態(tài)方法getRequestAttributes獲取Request實(shí)例顿苇,是因?yàn)門(mén)hreadLocal獲取。一個(gè)請(qǐng)求到達(dá)容器后税弃,Spring會(huì)把該請(qǐng)求Request實(shí)例通過(guò)setRequestAttributes方法 把Request實(shí)例放入該請(qǐng)求線(xiàn)程內(nèi)ThreadLocalMap中纪岁,然后就可以通過(guò)靜態(tài)方法取到。原理就是ThreadLocal则果,但ThreadLocal不能讓子線(xiàn)程繼承ThreadLocalMap信息幔翰,可以使用InherbritableThreadLocal
實(shí)現(xiàn)子線(xiàn)程信息傳遞。
但Spring Boot 默認(rèn)使用ThreadLocal把Request設(shè)置進(jìn)請(qǐng)求線(xiàn)程中西壮,這樣如果在請(qǐng)求方法里面另起一個(gè)子線(xiàn)程然后再通過(guò)getRequestAttributes方法獲取遗增,是獲取不到的。
所以要在能讓子線(xiàn)程獲取到款青,就可以使用InherbritableThreadLocal做修,看setRequestAttributes方法有這個(gè)布爾值可以設(shè),至于在哪里設(shè)就沒(méi)去深究抡草。但個(gè)人認(rèn)為最好不要修改該布爾值饰及,默認(rèn)就行,否則會(huì)有意向不到的可能