Java子線程調(diào)用RequestContextHolder.getRequestAttributes()方法問(wèn)題詳解
相信很多開(kāi)發(fā)過(guò)程中都用過(guò)RequestContextHolder.getRequestAttributes(),沒(méi)錯(cuò),我也經(jīng)常用,但今天出現(xiàn)了問(wèn)題,獲取到的實(shí)例是空的
原因是因?yàn)槲倚麻_(kāi)了一個(gè)子線程,在子線程調(diào)用了RequestContextHolder.getRequestAttributes()。實(shí)際獲取到的是空的
然后查看了源碼
ThreadLocal獲取。一個(gè)請(qǐng)求到達(dá)容器后,Spring會(huì)把該請(qǐng)求Request實(shí)例通過(guò)setRequestAttributes方法 把Request實(shí)例放入該請(qǐng)求線程內(nèi)ThreadLocalMap中,然后就可以通過(guò)靜態(tài)方法取到。原理就是ThreadLocal,但ThreadLocal不能讓子線程繼承ThreadLocalMap信息,可以使用InherbritableThreadLocal
實(shí)現(xiàn)子線程信息傳遞。
Spring Boot 默認(rèn)使用ThreadLocal把Request設(shè)置進(jìn)請(qǐng)求線程中,這樣如果在請(qǐng)求方法里面另起一個(gè)子線程然后再通過(guò)getRequestAttributes方法獲取,是獲取不到的
如果想要在子線程獲取,設(shè)置inheritable=true即可,但我一直沒(méi)找到在哪里可以設(shè)置,于是自己就寫(xiě)了個(gè)工具類(lèi)來(lái)讓子線程獲取,思路是自定義一個(gè)注解,攔截注解,將父線程的ServletRequestAttributes給InheritableThreadLocal,然后在子線程即可獲取
package com.shinedata.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface RequestInheritableThread { }
AOP
package com.shinedata.aop; import com.shinedata.util.context.RequestHolder; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; import org.springframework.web.context.request.ServletRequestAttributes; @Aspect @Component public class RequestHolderAspect { @Before("@annotation(com.shinedata.annotation.RequestInheritableThread)") public void doBefore(JoinPoint joinPoint) { ServletRequestAttributes servletRequestAttributes = RequestHolder.getServletRequestAttributes(); RequestHolder.setServletRequestAttributes(servletRequestAttributes); } @After("@annotation(com.shinedata.annotation.RequestInheritableThread)") public void doAfter(JoinPoint joinPoint) { RequestHolder.removeServletRequestAttributes(); } }
工具類(lèi)
package com.shinedata.util.context; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; /** * @ClassName RequestHolder * @Author yupanpan */ public class RequestHolder { private static final Logger logger = LoggerFactory.getLogger(RequestHolder.class); public static final String GET = "GET"; public static final String POST = "POST"; public static final String UTF8 = "UTF-8"; public static InheritableThreadLocal<ServletRequestAttributes> servletRequestAttributesInheritableThreadLocal= new InheritableThreadLocal(); public static HttpServletRequest getRequest() { return getServletRequestAttributes().getRequest(); } public static HttpServletResponse getResponse() { return getServletRequestAttributes().getResponse(); } public static ServletRequestAttributes setServletRequestAttributes(ServletRequestAttributes servletRequestAttributes) { servletRequestAttributesInheritableThreadLocal.set(servletRequestAttributes); return servletRequestAttributes; } public static void removeServletRequestAttributes() { servletRequestAttributesInheritableThreadLocal.remove(); } public static ServletRequestAttributes getServletRequestAttributes() { ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); if(requestAttributes==null){ requestAttributes=servletRequestAttributesInheritableThreadLocal.get(); } return requestAttributes; } }
需要在子線程獲取ServletRequestAttributes使用的時(shí)候在父方法貼個(gè)注解@RequestInheritableThread就行了,父方法里面即使開(kāi)子線程,子線程里面也能獲取ServletRequestAttributes
補(bǔ)充第二種解決辦法,在開(kāi)啟新線程之前,將RequestAttributes對(duì)象設(shè)置為子線程共享
//開(kāi)啟新線程之前,添加代碼: ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); RequestContextHolder.setRequestAttributes(sra, true);
到此這篇關(guān)于Java子線程調(diào)用RequestContextHolder.getRequestAttributes()方法問(wèn)題詳解的文章就介紹到這了,更多相關(guān)Java RequestContextHolder.getRequestAttributes()內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA新建Springboot項(xiàng)目(圖文教程)
下面小編就為大家?guī)?lái)一篇IDEA新建Springboot項(xiàng)目(圖文教程)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07詳解java如何處理各種批量數(shù)據(jù)入庫(kù)
這篇文章主要為大家詳細(xì)介紹了java如何使用BlockingQueue處理各種批量數(shù)據(jù)入庫(kù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11Java后臺(tái)返回和處理JSon數(shù)據(jù)的方法步驟
這篇文章主要介紹了Java后臺(tái)返回和處理JSon數(shù)據(jù)的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09利用Spring插件實(shí)現(xiàn)策略模式的案例詳解
Spring插件提供了一種更實(shí)用的插件開(kāi)發(fā)方法,它提供了插件實(shí)現(xiàn)擴(kuò)展核心系統(tǒng)功能的核心靈活性,但當(dāng)然不提供核心OSGi功能,如動(dòng)態(tài)類(lèi)加載或運(yùn)行時(shí)安裝和部署插件,本文就來(lái)聊下如何使用spring插件來(lái)實(shí)現(xiàn)策略模式,需要的朋友可以參考下2023-05-05SpringBoot異常處理器的使用與添加員工功能實(shí)現(xiàn)流程介紹
設(shè)計(jì)完了登錄與退出功能還只完成了冰山一角,經(jīng)過(guò)測(cè)試發(fā)現(xiàn),我們以u(píng)rl的方式來(lái)訪問(wèn)網(wǎng)站時(shí)可以直接跳過(guò)登陸頁(yè)面進(jìn)入后臺(tái)頁(yè)面,這樣顯然是不合理的,下面我們通過(guò)異常攔截器+boot來(lái)做到訪問(wèn)限制,以及實(shí)現(xiàn)新增員工功能,制作全局異常處理器2022-10-10SpringBoot項(xiàng)目運(yùn)行jar包啟動(dòng)的步驟流程解析
這篇文章主要介紹了SpringBoot項(xiàng)目運(yùn)行jar包啟動(dòng)的步驟流程,本文分步驟通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-07-07Java實(shí)現(xiàn)二叉搜索樹(shù)的插入、刪除功能
這篇文章主要介紹了Java實(shí)現(xiàn)二叉搜索樹(shù)的插入、刪除,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01