這幾天調(diào)試項(xiàng)目购桑,突然對(duì)于http請(qǐng)求如何傳到我的項(xiàng)目,然后找到對(duì)應(yīng)的控制方法產(chǎn)生了興趣菠隆,這部分的內(nèi)容比較多兵琳,本來(lái)想用一篇長(zhǎng)文來(lái)測(cè)試總結(jié)狂秘,現(xiàn)在還是先分點(diǎn)去記錄。
首先先介紹一下請(qǐng)求傳輸?shù)巾?xiàng)目的流程躯肌。Http請(qǐng)求是屬于應(yīng)用層的者春,請(qǐng)求的傳輸其實(shí)是在傳輸層,通過(guò)socket進(jìn)行傳輸?shù)絫omcat.
先介紹一些關(guān)于tomcat的知識(shí)清女。
tomcat目前的傳輸協(xié)議有四種钱烟,分別是:BIO, NIO,NIO2,APR
BIO協(xié)議依賴(lài)的類(lèi)是org.apache.coyote.http11.Http11Protocol
這個(gè)協(xié)議下,分為兩部分嫡丙,第一部分是acceptor拴袭,用于接收socket請(qǐng)求的
第二部分是worker線程池,用于處理socket請(qǐng)求的
BIO是阻塞流的IO,acceptor接收socket請(qǐng)求后發(fā)送給worker線程池處理曙博,如果線程池沒(méi)有空閑的線程拥刻,那么acceptor就會(huì)發(fā)生阻塞。
NIO協(xié)議依賴(lài)的類(lèi)是org.apache.coyote.http11.Http11NioProtocol
這個(gè)協(xié)議在BIO的基礎(chǔ)上增加了一個(gè)poller設(shè)置
acceptor接收socket請(qǐng)求后不是直接發(fā)送給線程池父泳,而是發(fā)送給poller般哼。在Poller中,維護(hù)了一個(gè)Selector對(duì)象惠窄;當(dāng)Poller從隊(duì)列中取出socket后蒸眠,注冊(cè)到該Selector中;然后通過(guò)遍歷Selector睬捶,找出其中可讀的socket黔宛,并使用Worker中的線程處理相應(yīng)請(qǐng)求。
通過(guò)BIO和NIO的描述擒贸,可以看出NIO在接收socket和處理socket的過(guò)程中臀晃,仍然會(huì)出現(xiàn)阻塞,不同的是介劫,NIO增加了一個(gè)Poller徽惋,基于這個(gè)區(qū)別,如果tomcat在高分發(fā)的情況下座韵,使用NIO可以提高Tomcat的工作效率
目前大多數(shù)HTTP請(qǐng)求使用的是長(zhǎng)連接(HTTP/1.1默認(rèn)keep-alive為true)险绘,而長(zhǎng)連接意味著,一個(gè)TCP的socket在當(dāng)前請(qǐng)求結(jié)束后誉碴,如果沒(méi)有新的請(qǐng)求到來(lái)宦棺,socket不會(huì)立馬釋放,而是等timeout后再釋放黔帕。如果使用BIO代咸,“讀取socket并交給Worker中的線程”這個(gè)過(guò)程是阻塞的,也就意味著在socket等待下一個(gè)請(qǐng)求或等待釋放的過(guò)程中成黄,處理這個(gè)socket的工作線程會(huì)一直被占用呐芥,無(wú)法釋放逻杖;因此Tomcat可以同時(shí)處理的socket數(shù)目不能超過(guò)最大線程數(shù),性能受到了極大限制思瘟。而使用NIO荸百,“讀取socket并交給Worker中的線程”這個(gè)過(guò)程是非阻塞的,當(dāng)socket在等待下一個(gè)請(qǐng)求或等待釋放時(shí)滨攻,并不會(huì)占用工作線程够话,因此Tomcat可以同時(shí)處理的socket數(shù)目遠(yuǎn)大于最大線程數(shù),并發(fā)性能大大提高光绕。
APR協(xié)議依賴(lài)的類(lèi)是org.apache.coyote.http11.Http11AprProtocol
APR是Apache Portable Runtime更鲁,是Apache可移植運(yùn)行庫(kù),利用本地庫(kù)可以實(shí)現(xiàn)高可擴(kuò)展性奇钞、高性能;Apr是在Tomcat上運(yùn)行高并發(fā)應(yīng)用的首選模式
配置相對(duì)應(yīng)的協(xié)議:
配置BIO的
<Connector port="8080" protocol="org.apache.coyote.http11.Http11Protocol "
connectionTimeout="20000"
redirectPort="8443" />
配置NIO的
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol "
connectionTimeout="20000"
redirectPort="8443" />
配置APR的
<Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol "
connectionTimeout="20000"
redirectPort="8443" />
注:Tomcat7中支持這3種漂坏,Tomcat8增加了對(duì)NIO2的支持景埃,而到了Tomcat8.5和Tomcat9.0,則去掉了對(duì)BIO的支持顶别。
如果沒(méi)有指定Protocal,則使用默認(rèn)值HTTP/1.1,其含義如下:在Tomcat7中谷徙,自動(dòng)選取使用BIO或APR(如果找到ARP需要的本地庫(kù),則使用ARP,否則使用BIO驯绎;在Tomcat8,自動(dòng)選取使用NIO或ARP(如果找到APR需要的本地庫(kù)完慧,則使用APR,否則使用NIO剩失。
參考鏈接:http://www.cnblogs.com/kismetv/p/7806063.html
socket請(qǐng)求經(jīng)過(guò)tomcat的線程處理后屈尼,會(huì)封裝成RequestFacade然后到web.xml配置文件
通過(guò)配置先初始化springMVC容器,進(jìn)入DispatcherServlet類(lèi)拴孤,進(jìn)行相關(guān)的初始化工作脾歧,這部分初始化我只了解了一點(diǎn)點(diǎn),先將我現(xiàn)在理解的寫(xiě)出來(lái)演熟,進(jìn)入這個(gè)類(lèi)鞭执,會(huì)先調(diào)用它的有參或者無(wú)參構(gòu)造方法,然后進(jìn)入它的父類(lèi)或者子類(lèi)的方法初始化spring容器芒粹,將handlerMethod和url進(jìn)行關(guān)聯(lián)兄纺,也就是在dispatcherServlet中有個(gè)onRefresh方法會(huì)執(zhí)行初始化方法initStrategies
該方法的結(jié)構(gòu)如下:
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
我了解的是HandlerMappings這部分,將handlerMappings初始化好會(huì)先調(diào)用一開(kāi)始結(jié)構(gòu)圖中的doService方法化漆,然后才到doDispatch方法估脆,doDispatch方法是springMVC的核心方法之一。
rotected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
//判斷request是否是文件上傳的
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//通過(guò)HandlerMappings迭代器获三,選出合適的HandlerMapping旁蔼,然后調(diào)用這個(gè)對(duì)象的getHandler獲取Handler的執(zhí)行鏈
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//mappedHandler.getHandler()類(lèi)型是Object锨苏,但是可以轉(zhuǎn)換成HandlerMethod,之前初始化講到過(guò)這個(gè)部分
//通過(guò)handlerAdapter的方法supports來(lái)獲取相對(duì)應(yīng)的handlerAdapter(Hanlder的適配器)
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (this.logger.isDebugEnabled()) {
this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
//測(cè)試所有的攔截器,有一個(gè)失敗棺聊,則請(qǐng)求失敗
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//綁定請(qǐng)求到視圖上
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
//傳輸?shù)角岸隧?yè)面
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
private void applyDefaultViewName(HttpServletRequest request, ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
mv.setViewName(this.getDefaultViewName(request));
}
}
這部分代碼最重要的就是獲取HandlerExecutorChain和HandlerAdaptor這倆部分
HandlerExecutorChain是Handler的執(zhí)行鏈伞租,獲取對(duì)應(yīng)handler
首先進(jìn)行迭代HandlerMappings,然后根據(jù)傳入的request,調(diào)用HandlerMapping的方法getHandler(request),
該方法在AbstrateHandlerMapping類(lèi)下限佩,具體的代碼如下:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//獲取內(nèi)部的handler
Object handler = this.getHandlerInternal(request);
if (handler == null) {
handler = this.getDefaultHandler();
}
if (handler == null) {
return null;
} else {
if (handler instanceof String) {
String handlerName = (String)handler;
handler = this.getApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
CorsConfiguration config = globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig;
executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
}
這部分代碼中有個(gè)方法getHandlerInternal(request)方法是為了獲取內(nèi)部的handler的葵诈,這個(gè)方法在AbstrateHandlerMethodMapping類(lèi)中,這部分代碼如下:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//獲取request映射到的url地址
String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Looking up handler method for path " + lookupPath);
}
//設(shè)置讀鎖
this.mappingRegistry.acquireReadLock();
HandlerMethod var4;
try {
//取出該地址相關(guān)的handlerMethod
HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
if (this.logger.isDebugEnabled()) {
if (handlerMethod != null) {
this.logger.debug("Returning handler method [" + handlerMethod + "]");
} else {
this.logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
var4 = handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
} finally {
this.mappingRegistry.releaseReadLock();
}
return var4;
}
之前初始化的時(shí)候祟同,我們說(shuō)了作喘,會(huì)將url和handler關(guān)聯(lián)起來(lái),這里代碼里的handlerMethod其實(shí)就是handler如下圖:
這個(gè)方法返回的變量也是設(shè)置成為了handler
獲取內(nèi)部handler方法getHandlerInternal(request)這部分講完了晕城,下一部分是方法getHandlerExecutionChain(handler,request),該方法是將相關(guān)的攔截器關(guān)聯(lián)到這個(gè)handler上泞坦,代碼如下:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = handler instanceof HandlerExecutionChain ? (HandlerExecutionChain)handler : new HandlerExecutionChain(handler);
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
Iterator var5 = this.adaptedInterceptors.iterator();
while(var5.hasNext()) {
HandlerInterceptor interceptor = (HandlerInterceptor)var5.next();
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
} else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
到這里就完成了一個(gè)handlerExcetionChain的創(chuàng)建,下一步是找到相關(guān)的handler適配器
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator var2 = this.handlerAdapters.iterator();
HandlerAdapter ha;
do {
if (!var2.hasNext()) {
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
ha = (HandlerAdapter)var2.next();
if (this.logger.isTraceEnabled()) {
this.logger.trace("Testing handler adapter [" + ha + "]");
}
} while(!ha.supports(handler));
return ha;
}
這個(gè)方法參數(shù)是將前面獲得的handler執(zhí)行鏈的handler砖顷,調(diào)用handlerExcutionChain.getHandler()的方法返回一個(gè)object類(lèi)型
進(jìn)入handlerAdapter.supports(handler)方法
前面的條件是判斷handler是否是HandlerMethod的實(shí)例贰锁,這個(gè)地方我們回想一下,在前邊獲取HandlerMethod的時(shí)候滤蝠,我們將這個(gè)類(lèi)型轉(zhuǎn)換成了Object類(lèi)型豌熄,那么考慮一下,現(xiàn)在這種情況下物咳,handler是否依然是HandlerMethod的實(shí)例呢锣险?答案是肯定的,還是HandlerMethod的實(shí)例
測(cè)試代碼如下:
控制臺(tái)輸出如下:
因?yàn)镺bject是所有類(lèi)的父類(lèi)览闰,所以可以子類(lèi)轉(zhuǎn)換成父類(lèi)芯肤,也可以將父類(lèi)轉(zhuǎn)換成之前對(duì)應(yīng)的子類(lèi)。是同一個(gè)實(shí)例
好了言歸正傳压鉴,這個(gè)方法還有后一個(gè)條件纷妆,supportsInternal((HandlerMethod)handler)這個(gè)方法的代碼如下:
這個(gè)條件的作用是干嘛的,我現(xiàn)在還不清楚晴弃,看方法里的意思是只要是handlerMethod的參數(shù)掩幢,就會(huì)返回一個(gè)true
經(jīng)過(guò)這兩個(gè)條件的判斷,返回一個(gè)布爾值到getHandlerAdapter方法中上鞠,主要找到一個(gè)適配器际邻,就會(huì)自動(dòng)跳出循環(huán),返回handler適配器芍阎。
handler的執(zhí)行鏈和handler的適配器都有的情況下世曾,接下來(lái)是將handler執(zhí)行鏈?zhǔn)欠衲軌虺晒νㄟ^(guò)攔截器,只要有一個(gè)失敗谴咸,那么此次請(qǐng)求失敗轮听,代碼語(yǔ)句是:
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
然后通過(guò)handler適配器進(jìn)行視圖的處理骗露,handlerAdapter.handle(processedRequest, response, mappedHandler.getHandler())
返回一個(gè)ModelAndView的對(duì)象
最后通過(guò)
processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
方法將mv對(duì)象反饋到前端頁(yè)面。
總結(jié)
上邊說(shuō)了那么多血巍,如果沒(méi)看過(guò)源碼萧锉,應(yīng)該看起來(lái)比較吃力,下邊將本次的內(nèi)容進(jìn)行總結(jié)
1述寡、http請(qǐng)求在tcp層被封裝成socket請(qǐng)求發(fā)送給tomcat柿隙,tomcat用acceptor來(lái)接收socket請(qǐng)求,然后根據(jù)具體的協(xié)議來(lái)進(jìn)行相對(duì)應(yīng)的處理鲫凶,再交付給線程禀崖,tomcat將request打包成requestFacade,經(jīng)過(guò)doFilter重重的過(guò)濾器螟炫,到達(dá)HttpServlet類(lèi)波附,調(diào)用service(request,response)方法傳入請(qǐng)求和響應(yīng)對(duì)象,判斷請(qǐng)求類(lèi)型昼钻,然后調(diào)用doGet或者doPost方法叶雹,接著到FrameworkServlet的processRequest()方法中,調(diào)用doService(),再在這個(gè)方法中調(diào)用doDispatch()方法换吧,至此,完成了請(qǐng)求到分發(fā)器的步驟钥星。
2沾瓦、請(qǐng)求到達(dá)分發(fā)器,先判斷請(qǐng)求是否是文件上傳谦炒,文件上傳則請(qǐng)求失敗贯莺,否則通過(guò)HandlerMappings迭代器迭代出當(dāng)前請(qǐng)求對(duì)應(yīng)的handlerMapping,判斷是否匹配請(qǐng)求,需要調(diào)用handlerMapping.getHandler(request)方法宁改,在這個(gè)方法首先調(diào)用getHandlerInternal(request)判斷根據(jù)路徑是否能夠獲取到對(duì)應(yīng)的handler(handlerMethod)缕探,如果不能獲取到,則設(shè)置一個(gè)默認(rèn)的handler还蹲,注意在這個(gè)方法里邊加了一層讀鎖爹耗,接著調(diào)用getHandlerExecutionChain(handler,request)方法獲取handler的執(zhí)行鏈,這個(gè)方法實(shí)際上是給handler執(zhí)行鏈增加攔截器的作用谜喊,然后判斷一下是否跨域潭兽,最后返回匹配好的handler執(zhí)行鏈
3、handler執(zhí)行鏈獲取成功后斗遏,進(jìn)行第三個(gè)重要的步驟山卦,獲取handler的適配器,調(diào)用getHandlerAdapter()方法蟋座,傳入handlerExecutionChain.getHandler()作為參數(shù)夺荒,獲取適配器方法的內(nèi)部使用do...while(條件)的方式迭代出匹配的適配器速梗,判斷是否匹配钞啸,使用HandlerAdapter.supports(handler)作為條件,這個(gè)方法返回的是一個(gè)布爾值铸本,如果傳入的handler是HandlerMethod的實(shí)例肮雨,則返回true,跳出循環(huán)归敬,匹配成功酷含。
4、關(guān)于ModelAndView這塊汪茧,這部分內(nèi)容是根據(jù)HandlerExecutionChain和HandlerAdapter二者進(jìn)行進(jìn)一步的判定椅亚,然后反饋給前端信息,這部分我還沒(méi)怎么看舱污,現(xiàn)在無(wú)法總結(jié)呀舔。