由RequestContextHolder獲取HttpServletRequest引發的思考

 最近做項目中需要獲取request,從而獲取請求ip,request可以從控制層傳過來,也可以調用RequestContextHolder獲取。獲取方式如下:

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

 那麼問題來了,問什麼調用這個方法就可以呢,值又是什麼時候set進去的呢,首先要明確這是springMvc提供的方法,所以得從springMvc的源碼看起,spring Mvc核心代碼圖譜如下:
springMvc核心代碼繼承關係圖譜
http請求會出發HttpServlet的doGet或doPost方法,但這兩個方法的實現是在FrameworkServlet中,所以看FrameworkServlet源碼,先看doGet方法入手

protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response); 
	}

只有個processRequest方法,那麼重點看下這個方法

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;
		//先獲取LocaleContext
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		//建立新的LocaleContext
		LocaleContext localeContext = buildLocaleContext(request);
		//建立新的ServletRequestAttributes   這個就是存在後來RequestContextHolder的RequestAttributes,
		//所以取出來的時候需要強轉一下,獲取RequestContextHolder的HttpServletRequest
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
		//具體方法
		initContextHolders(request, localeContext, requestAttributes);

		try {
			doService(request, response); //這是DispatcherServlet核心實現代碼,有興趣的可以看下,這裏不做討論
		}
		catch (ServletException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (IOException ex) {
			failureCause = ex;
			throw ex;
		}
		………… 此處省略
		
		}
	}

接下來看下其中的initContextHolders方法到底做了什麼

private void initContextHolders(
			HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {

		if (localeContext != null) {
			LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
		}
		if (requestAttributes != null) {  //設置的地方requestAttributes
			RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Bound request context to thread: " + request);
		}
	}


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();
			}
		}
	}

inheritableRequestAttributesHolder和requestAttributesHolder其實就是RequestContextHolder的屬性,就在這個時候被set進去了,

//得到存儲進去的request
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
			new NamedThreadLocal<RequestAttributes>("Request attributes");

//可被子線程繼承的request
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
			new NamedInheritableThreadLocal<RequestAttributes>("Request context");

這裏需要對ThreadLocal簡單解釋一下,ThreadLocal即線程共享變量,線程間互不影響,所以在每個請求裏獲取的就是當前線程的request.對ThreadLocal的詳細解釋請看這裏

  到這裏應該對之前獲取的request不足爲奇了,希望能幫到愛學習的你!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章