03.01 使用RequestContextHolder.getRequestAttributes()的注意事項

注意事項:

  1. 不要在請求方法裡另起一個子線程調用該方法;
  2. 在請求週期中,儘可能不要傳遞Request實例給多線程使用,因為子線程可能在Request生命週期結束銷燬後再使用Request時獲取不了參數,否則必須同步線程 讓其在生命週期結束前調用;

在Spring Boot中,如果我們要獲取當前Request實例,可以通過以下這個方法獲取。

<code>HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();/<code>

使用這種方法獲取的時候需要注意使用多線程會出現些狀況,例如一個請求過來後,請求達到Service方法,然後Service方法裡另起一個線程啟動,在該線程run方法裡面想要通過以上方法可能獲取不到Request實例。
且看RequestContextHolder內部分源碼:

<code>    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;    }/<requestattributes>/<requestattributes>/<code>

可看到之所以能通過靜態方法getRequestAttributes獲取Request實例,是因為ThreadLocal獲取。一個請求到達容器後,Spring會把該請求Request實例通過setRequestAttributes方法 把Request實例放入該請求線程內ThreadLocalMap中,然後就可以通過靜態方法取到。原理就是ThreadLocal,但ThreadLocal不能讓子線程繼承ThreadLocalMap信息,可以使用InherbritableThreadLocal實現子線程信息傳遞。 但Spring Boot 默認使用ThreadLocal把Request設置進請求線程中,這樣如果在請求方法裡面另起一個子線程然後再通過getRequestAttributes方法獲取,是獲取不到的。

所以要在能讓子線程獲取到,就可以使用InherbritableThreadLocal,看setRequestAttributes方法有這個布爾值可以設,至於在哪裡設就沒去深究。但個人認為最好不要修改該布爾值,默認就行,否則會有意向不到的可能


分享到:


相關文章: