轉(zhuǎn)自:https://zhuanlan.zhihu.com/p/24293062?refer=dreawer
1.RequestContextHolder的使用
RequestContextHolder顧名思義,持有上下文的Request容器.使用是很簡(jiǎn)單的,具體使用如下:
//兩個(gè)方法在沒(méi)有使用JSF的項(xiàng)目中是沒(méi)有區(qū)別的RequestAttributesrequestAttributes=RequestContextHolder.currentRequestAttributes();//? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? RequestContextHolder.getRequestAttributes();//從session里面獲取對(duì)應(yīng)的值Stringstr=(String)requestAttributes.getAttribute("name",RequestAttributes.SCOPE_SESSION);HttpServletRequestrequest=((ServletRequestAttributes)requestAttributes).getRequest();HttpServletResponseresponse=((ServletRequestAttributes)requestAttributes).getResponse();
看到這一般都會(huì)想到幾個(gè)問(wèn)題:
request和response怎么和當(dāng)前請(qǐng)求掛鉤?
request和response等是什么時(shí)候設(shè)置進(jìn)去的?
2.解決疑問(wèn)
2.1 request和response怎么和當(dāng)前請(qǐng)求掛鉤?
首先分析RequestContextHolder這個(gè)類,里面有兩個(gè)ThreadLocal保存當(dāng)前線程下的request,關(guān)于ThreadLocal可以參考我的另一篇博文[Java學(xué)習(xí)記錄--ThreadLocal使用案例]
//得到存儲(chǔ)進(jìn)去的requestprivatestaticfinalThreadLocal<RequestAttributes>requestAttributesHolder=newNamedThreadLocal<RequestAttributes>("Request attributes");//可被子線程繼承的requestprivatestaticfinalThreadLocal<RequestAttributes>inheritableRequestAttributesHolder=newNamedInheritableThreadLocal<RequestAttributes>("Request context");
再看`getRequestAttributes()`方法,相當(dāng)于直接獲取ThreadLocal里面的值,這樣就保證了每一次獲取到的Request是該請(qǐng)求的request.
publicstaticRequestAttributesgetRequestAttributes(){RequestAttributesattributes=requestAttributesHolder.get();if(attributes==null){attributes=inheritableRequestAttributesHolder.get();}returnattributes;}
2.2request和response等是什么時(shí)候設(shè)置進(jìn)去的?
找這個(gè)的話需要對(duì)springMVC結(jié)構(gòu)的`DispatcherServlet`的結(jié)構(gòu)有一定了解才能準(zhǔn)確的定位該去哪里找相關(guān)代碼.
在IDEA中會(huì)顯示如下的繼承關(guān)系.

左邊1這里是Servlet的接口和實(shí)現(xiàn)類.
右邊2這里是使得SpringMVC具有Spring的一些環(huán)境變量和Spring容器.類似的XXXAware接口就是對(duì)該類提供Spring感知,簡(jiǎn)單來(lái)說(shuō)就是如果想使用Spring的XXXX就要實(shí)現(xiàn)XXXAware,spring會(huì)把需要的東西傳送過(guò)來(lái).
那么剩下要分析的的就是三個(gè)類,簡(jiǎn)單看下源碼
1. HttpServletBean 進(jìn)行初始化工作
2. FrameworkServlet 初始化 WebApplicationContext,并提供service方法預(yù)處理請(qǐng)
3. DispatcherServlet 具體分發(fā)處理.
那么就可以在FrameworkServlet查看到該類重寫了service(),doGet(),doPost()...等方法,這些實(shí)現(xiàn)里面都有一個(gè)預(yù)處理方法`processRequest(request, response);`,所以定位到了我們要找的位置
查看`processRequest(request, response);`的實(shí)現(xiàn),具體可以分為三步:
獲取上一個(gè)請(qǐng)求的參數(shù)
重新建立新的參數(shù)
設(shè)置到XXContextHolder
父類的service()處理請(qǐng)求
恢復(fù)request
發(fā)布事件
protectedfinalvoidprocessRequest(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{longstartTime=System.currentTimeMillis();ThrowablefailureCause=null;//獲取上一個(gè)請(qǐng)求保存的LocaleContextLocaleContextpreviousLocaleContext=LocaleContextHolder.getLocaleContext();//建立新的LocaleContextLocaleContextlocaleContext=buildLocaleContext(request);//獲取上一個(gè)請(qǐng)求保存的RequestAttributesRequestAttributespreviousAttributes=RequestContextHolder.getRequestAttributes();//建立新的RequestAttributesServletRequestAttributesrequestAttributes=buildRequestAttributes(request,response,previousAttributes);WebAsyncManagerasyncManager=WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),newRequestBindingInterceptor());//具體設(shè)置的方法initContextHolders(request,localeContext,requestAttributes);try{doService(request,response);}catch(ServletExceptionex){failureCause=ex;throwex;}catch(IOExceptionex){failureCause=ex;throwex;}catch(Throwableex){failureCause=ex;thrownewNestedServletException("Request processing failed",ex);}finally{//恢復(fù)resetContextHolders(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");}}}//發(fā)布事件publishRequestHandledEvent(request,response,startTime,failureCause);}}
再看initContextHolders(request, localeContext, requestAttributes)方法,把新的RequestAttributes設(shè)置進(jìn)LocalThread,實(shí)際上保存的類型為ServletRequestAttributes,這也是為什么在使用的時(shí)候可以把RequestAttributes強(qiáng)轉(zhuǎn)為ServletRequestAttributes.
privatevoidinitContextHolders(HttpServletRequestrequest,LocaleContextlocaleContext,RequestAttributesrequestAttributes){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);}}
因此RequestContextHolder里面最終保存的為ServletRequestAttributes,這個(gè)類相比`RequestAttributes`方法是多了很多.
