javascript
ThreadLocal在Spring中的应用
共享對象
- 如果在每個線程中ThreadLocal.set()進(jìn)去的東西本來就是多線程共享的同一個對象,比如static對象,那么多個線程的ThreadLocal.get()取得的還是這個共享對象本身,還是有并發(fā)訪問問題
如果不使用ThreadLocal就可以解決問題,那么就不要強(qiáng)行使用
- 例如在任務(wù)很少的時候,在局部變量中可以新建對象就可以解決問題,那么就不需要使用到ThreadLocal
優(yōu)先使用框架的支持,而不是自己創(chuàng)造
- 例如在Spring中,如果可以使用RequestContextHolder,那么就不需要自己維護(hù)ThreadLocal,因?yàn)樽约嚎赡軙浾{(diào)用remove()方法,造成內(nèi)存泄漏
ThreadLocal在Spring中的實(shí)例分析
- RequestContextHolder和DateTimeContextHolder中,看到里面使用了ThreadLocal
- 每次HTTP請求都對應(yīng)一個線程,線程之間相互隔離,這就是ThreadLocal的典型應(yīng)用場景
在Web開發(fā)中,service層或者某個工具類中需要獲取到HttpServletRequest對象還是比較常見的。一種方式是將HttpServletRequest作為方法的參數(shù)從controller層一直放下傳遞,不過這種有點(diǎn)費(fèi)勁,且做起來不是優(yōu)雅;還有另一種則是RequestContextHolder,直接在需要用的地方使用如下方式取HttpServletRequest即可,使用代碼如下:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();要理解上面的為何可以這么使用,需要理解兩個問題:
對于第1個問題,熟悉ThreadLocal的人應(yīng)該很容易看出來這個是ThreadLocal的應(yīng)用,這個類的原理在(ThreadLocal原理)有講到,其實(shí)很類似上篇博文文末提到的UserContextHolder。
第2個問題應(yīng)該屬于spring-mvc的問題,這個是在spring-mvc執(zhí)行時設(shè)置進(jìn)去的
源碼分析
首先我們先來看下RequestContextHolder的源碼,源碼如下:
public abstract class RequestContextHolder {private static final ThreadLocal<RequestAttributes> requestAttributesHolder =new NamedThreadLocal<RequestAttributes>("Request attributes");private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =new NamedInheritableThreadLocal<RequestAttributes>("Request context");public static void resetRequestAttributes() {requestAttributesHolder.remove();inheritableRequestAttributesHolder.remove();}public static void setRequestAttributes(RequestAttributes attributes) {setRequestAttributes(attributes, false);}//將RequestAttributes對象放入到ThreadLocal中,而HttpServletRequest和HttpServletResponse等則封裝在RequestAttributes對象中,在此處就不對RequestAttributes這個類展開。反正我們需要知道的就是要獲取RequestAttributes對象,然后再從RequestAttributes對象中獲取到我們所需要的HttpServletRequest即可public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {if (attributes == null) {resetRequestAttributes();}else {if (inheritable) {inheritableRequestAttributesHolder.set(attributes);requestAttributesHolder.remove();}else {requestAttributesHolder.set(attributes);inheritableRequestAttributesHolder.remove();}}}public static RequestAttributes getRequestAttributes() {RequestAttributes attributes = requestAttributesHolder.get();if (attributes == null) {attributes = inheritableRequestAttributesHolder.get();}return attributes;}}那么在spring-mvc中是怎么實(shí)現(xiàn)的呢,我們來簡單分析的,想了解具體機(jī)制的可以去看看spring-mvc的源碼。
我們看下FrameworkServlet這個類,也就是DispatcherServlet的父類,里面有個processRequest方法,根據(jù)方法名稱我們也可以大概了解到這個是方法用于處理請求的。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {long startTime = System.currentTimeMillis();Throwable failureCause = null;LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());//將RequestAttributes設(shè)置到RequestContextHolderinitContextHolders(request, localeContext, requestAttributes);try {//具體的業(yè)務(wù)邏輯doService(request, response);}catch (ServletException ex) {failureCause = ex;throw ex;}catch (IOException ex) {failureCause = ex;throw ex;}catch (Throwable ex) {failureCause = ex;throw new NestedServletException("Request processing failed", ex);}finally {//重置RequestContextHolder之前設(shè)置RequestAttributesresetContextHolders(request, previousLocaleContext, previousAttributes);if (requestAttributes != null) {requestAttributes.requestCompleted();}if (logger.isDebugEnabled()) {if (failureCause != null) {this.logger.debug("Could not complete request", failureCause);}else {if (asyncManager.isConcurrentHandlingStarted()) {logger.debug("Leaving response open for concurrent processing");}else {this.logger.debug("Successfully completed request");}}}publishRequestHandledEvent(request, response, startTime, failureCause);}}private void initContextHolders(HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {if (localeContext != null) {LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);}if (requestAttributes != null) {RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);}if (logger.isTraceEnabled()) {logger.trace("Bound request context to thread: " + request);}}簡單看下源碼,我們可以知道HttpServletRequest是在執(zhí)行doService方法之前,也就是具體的業(yè)務(wù)邏輯前進(jìn)行設(shè)置的,然后在執(zhí)行完業(yè)務(wù)邏輯或者拋出異常時重置RequestContextHolder移除當(dāng)前的HttpServletRequest。
寫本文的目的主要是記錄下這個RequestContextHolder的使用,以及希望以后能在業(yè)務(wù)代碼中巧用該工具讓自己的代碼更加簡潔優(yōu)雅。
?
總結(jié)
以上是生活随笔為你收集整理的ThreadLocal在Spring中的应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ThreadLocal的空指针异常问题
- 下一篇: 内置锁的能力不足以满足需求