最近做項目中需要獲取request,從而獲取請求ip,request可以從控制層傳過來,也可以調用RequestContextHolder獲取。獲取方式如下:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest()
那麼問題來了,問什麼調用這個方法就可以呢,值又是什麼時候set進去的呢,首先要明確這是springMvc提供的方法,所以得從springMvc的源碼看起,spring Mvc核心代碼圖譜如下:
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不足爲奇了,希望能幫到愛學習的你!