日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

ThreadLocal在Spring中的应用

發布時間:2024/4/13 javascript 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ThreadLocal在Spring中的应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

共享對象

  • 如果在每個線程中ThreadLocal.set()進去的東西本來就是多線程共享的同一個對象,比如static對象,那么多個線程的ThreadLocal.get()取得的還是這個共享對象本身,還是有并發訪問問題

如果不使用ThreadLocal就可以解決問題,那么就不要強行使用

  • 例如在任務很少的時候,在局部變量中可以新建對象就可以解決問題,那么就不需要使用到ThreadLocal

優先使用框架的支持,而不是自己創造

  • 例如在Spring中,如果可以使用RequestContextHolder,那么就不需要自己維護ThreadLocal,因為自己可能會忘記調用remove()方法,造成內存泄漏

ThreadLocal在Spring中的實例分析

  • RequestContextHolder和DateTimeContextHolder中,看到里面使用了ThreadLocal
  • 每次HTTP請求都對應一個線程,線程之間相互隔離,這就是ThreadLocal的典型應用場景

在Web開發中,service層或者某個工具類中需要獲取到HttpServletRequest對象還是比較常見的。一種方式是將HttpServletRequest作為方法的參數從controller層一直放下傳遞,不過這種有點費勁,且做起來不是優雅;還有另一種則是RequestContextHolder,直接在需要用的地方使用如下方式取HttpServletRequest即可,使用代碼如下:

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

要理解上面的為何可以這么使用,需要理解兩個問題:

  • RequestContextHolder為什么能獲取到當前的HttpServletRequest
  • HttpServletRequest是在什么時候設置到RequestContextHolder
  • 對于第1個問題,熟悉ThreadLocal的人應該很容易看出來這個是ThreadLocal的應用,這個類的原理在(ThreadLocal原理)有講到,其實很類似上篇博文文末提到的UserContextHolder。

    第2個問題應該屬于spring-mvc的問題,這個是在spring-mvc執行時設置進去的

    源碼分析

    首先我們先來看下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中是怎么實現的呢,我們來簡單分析的,想了解具體機制的可以去看看spring-mvc的源碼。

    我們看下FrameworkServlet這個類,也就是DispatcherServlet的父類,里面有個processRequest方法,根據方法名稱我們也可以大概了解到這個是方法用于處理請求的。

    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設置到RequestContextHolderinitContextHolders(request, localeContext, requestAttributes);try {//具體的業務邏輯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之前設置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是在執行doService方法之前,也就是具體的業務邏輯前進行設置的,然后在執行完業務邏輯或者拋出異常時重置RequestContextHolder移除當前的HttpServletRequest。

    寫本文的目的主要是記錄下這個RequestContextHolder的使用,以及希望以后能在業務代碼中巧用該工具讓自己的代碼更加簡潔優雅。

    ?

    總結

    以上是生活随笔為你收集整理的ThreadLocal在Spring中的应用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。