SpringMVC中處理Http請(qǐng)求的原理詳解
SpingMVC處理Http請(qǐng)求原理
當(dāng)一個(gè)http請(qǐng)求過(guò)來(lái)了首先經(jīng)過(guò)的是DispatcherServlet這么一個(gè)前端控制器并調(diào)用了這個(gè)前端控制器的doService方法。
這個(gè)方法最終我們發(fā)現(xiàn)它調(diào)用了doDispatcher這么一個(gè)方法。這就是SpringMVC處理http請(qǐng)求的入口了。
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if(this.logger.isDebugEnabled()) {
String attributesSnapshot = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()?" resumed":"";
this.logger.debug("DispatcherServlet with name \'" + this.getServletName() + "\'" + attributesSnapshot + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
HashMap attributesSnapshot1 = null;
if(WebUtils.isIncludeRequest(request)) {
attributesSnapshot1 = new HashMap();
Enumeration inputFlashMap = request.getAttributeNames();
label112:
while(true) {
String attrName;
do {
if(!inputFlashMap.hasMoreElements()) {
break label112;
}
attrName = (String)inputFlashMap.nextElement();
} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
attributesSnapshot1.put(attrName, request.getAttribute(attrName));
}
}
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
if(this.flashMapManager != null) {
FlashMap inputFlashMap1 = this.flashMapManager.retrieveAndUpdate(request, response);
if(inputFlashMap1 != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap1));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
this.doDispatch(request, response);
} finally {
if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot1 != null) {
this.restoreAttributesAfterInclude(request, attributesSnapshot1);
}
}
}這個(gè)方法里面我們發(fā)現(xiàn)定義了一個(gè)ModelAndView將要返回的視圖與數(shù)據(jù),并且首先檢測(cè)這個(gè)請(qǐng)求是不是一個(gè)上傳的請(qǐng)求,然后根據(jù)這個(gè)請(qǐng)求獲取對(duì)應(yīng)的Handler,如果Handler不為空的話就根據(jù)這個(gè)handler獲取對(duì)應(yīng)的HandlerAdapter。接著調(diào)用攔截器鏈的所有前置攔截的這么一個(gè)方法,接著調(diào)用adapter的真正的處理方法處理請(qǐng)求,最后調(diào)用攔截器鏈中的所有后置攔截方法。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
//定義一個(gè)空的ModelAndView
ModelAndView err = null;
Object dispatchException = null;
try {
//檢查是否是上傳請(qǐng)求
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//獲取handler
mappedHandler = this.getHandler(processedRequest);
if(mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//根據(jù)handler獲取handlerAdapter
HandlerAdapter err1 = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if(isGet || "HEAD".equals(method)) {
long lastModified = err1.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;
}
}
//前置攔截
if(!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//真正處理
err = err1.handle(processedRequest, response, mappedHandler.getHandler());
if(asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, err);
//后置攔截
mappedHandler.applyPostHandle(processedRequest, response, err);
}
//后面一些catch finally語(yǔ)句省略。。。。。繼續(xù)跟進(jìn)這個(gè)getHandler方法看看具體是怎么實(shí)現(xiàn)的,發(fā)現(xiàn)就是循環(huán)遍歷最開(kāi)始初始化DispatcherServlet的時(shí)候初始化的那7個(gè)handlerMapping,去調(diào)用這些handlerMapping的getHandler方法;這里我們主要看這個(gè)RequestMappingHandlerMapping。


跟進(jìn)發(fā)現(xiàn)是調(diào)用了RequestMappingHandlerMapping的父類(lèi)AbstractHandlerMapping中的getHandler方法,并且這個(gè)方法又是去調(diào)用了getHandlerInternal來(lái)獲取Handler的。

繼續(xù)跟進(jìn)這個(gè)getHandlerInternal方法,首先它根據(jù)這個(gè)request獲取它的URI,接著通過(guò)uri獲取對(duì)應(yīng)的HandlerMethod對(duì)象最終返回了。
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Looking up handler method for path " + lookupPath);
}
this.mappingRegistry.acquireReadLock();
HandlerMethod var4;
try {
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;
}跟進(jìn)這個(gè)getLookupPathForRequest發(fā)現(xiàn)又調(diào)用了getPathWithinServletMapping方法。




這里我們就知道了這個(gè)lookupPath得到的就是這個(gè)請(qǐng)求的URI,拿到了這個(gè)URI接著就去調(diào)用lookupHandlerMethod方法了。

這里lookupHandlerMethod方法顧名思義就是通過(guò)這個(gè)URI根據(jù)請(qǐng)求的URI去尋找對(duì)應(yīng)的HandlerMethod。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
ArrayList matches = new ArrayList();
//通過(guò)這個(gè)uri去Map中查詢與那個(gè)HandlerMethod映射
List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if(directPathMatches != null) {
this.addMatchingMappings(directPathMatches, matches, request);
}
if(matches.isEmpty()) {
this.addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
//若查詢出這個(gè)uri有對(duì)應(yīng)的映射關(guān)系
if(!matches.isEmpty()) {
AbstractHandlerMethodMapping.MatchComparator comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request));
//對(duì)映射關(guān)系排序
matches.sort(comparator);
if(this.logger.isTraceEnabled()) {
this.logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
}
//獲取排序后第一個(gè)HandlerMethod
AbstractHandlerMethodMapping.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
if(matches.size() > 1) {
if(CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
AbstractHandlerMethodMapping.Match secondBestMatch = (AbstractHandlerMethodMapping.Match)matches.get(1);
if(comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path \'" + request.getRequestURL() + "\': {" + m1 + ", " + m2 + "}");
}
}
this.handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
} else {
return this.handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}最終得到了這么一個(gè)包含了Handler與這個(gè)URI具體處理方法的一個(gè)HandlerMethod對(duì)象。
注意現(xiàn)在這個(gè)handler可能還沒(méi)有被創(chuàng)建出來(lái)或者說(shuō)沒(méi)有得到,只是知道它的beanName。
最終還要調(diào)用這么一個(gè)createWithResolvedBean方法來(lái)得到。


獲得了這個(gè)handler之后,又繼續(xù)根據(jù)這個(gè)handler獲得了HandlerExecutionChain。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = this.getHandlerInternal(request);
if(handler == null) {
handler = this.getDefaultHandler();
}
if(handler == null) {
return null;
} else {
if(handler instanceof String) {
String executionChain = (String)handler;
handler = this.obtainApplicationContext().getBean(executionChain);
}
HandlerExecutionChain executionChain1 = 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;
executionChain1 = this.getCorsHandlerExecutionChain(request, executionChain1, config);
}
return executionChain1;
}
}所以得出最終結(jié)論getHandler這個(gè)方法最終所有通過(guò)這個(gè)URI返回了一個(gè)最開(kāi)始注冊(cè)的handler,然后通過(guò) 這個(gè)handler返回了一個(gè)包含這這個(gè)HandlerMethod以及一個(gè)攔截器鏈的這么一個(gè)對(duì)象。之后執(zhí)行前置攔截器,handler方法,后置攔截,完成。

到此這篇關(guān)于SpingMVC中處理Http請(qǐng)求的原理詳解的文章就介紹到這了,更多相關(guān)SpingMVC處理Http請(qǐng)求內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringMVC 中HttpMessageConverter簡(jiǎn)介和Http請(qǐng)求415 的問(wèn)題
- springMVC中HttpMessageConverter的具體使用
- SpringMVC配置javaConfig及StringHttpMessageConverter示例
- SpringMVC HttpMessageConverter消息轉(zhuǎn)換器
- SpringMVC @RequestBody自動(dòng)轉(zhuǎn)json Http415錯(cuò)誤的解決
- SpringMVC HttpMessageConverter報(bào)文信息轉(zhuǎn)換器
- SpringMVC如何自定義響應(yīng)的HTTP狀態(tài)碼
相關(guān)文章
IntelliJ IDEA將導(dǎo)入的項(xiàng)目轉(zhuǎn)成maven項(xiàng)目
這篇文章主要介紹了IntelliJ IDEA將導(dǎo)入的項(xiàng)目轉(zhuǎn)成maven項(xiàng)目,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
spring boot @PathVariable傳遞帶反斜杠參數(shù) / 的處理
這篇文章主要介紹了spring boot @PathVariable傳遞帶反斜杠參數(shù) / 的處理操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
Java-Redis-Redisson分布式鎖的功能使用及實(shí)現(xiàn)
這篇文章主要介紹了Java-Redis-Redisson-分布式鎖的功能使用及實(shí)現(xiàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08
Mybatis Order by動(dòng)態(tài)參數(shù)防注入方式
這篇文章主要介紹了Mybatis Order by動(dòng)態(tài)參數(shù)防注入方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04
Java AOP實(shí)現(xiàn)自定義滑動(dòng)窗口限流器方法詳解
這篇文章主要介紹了Java AOP實(shí)現(xiàn)自定義滑動(dòng)窗口限流器方法,其中滑動(dòng)窗口算法彌補(bǔ)了計(jì)數(shù)器算法的不足,滑動(dòng)窗口算法把間隔時(shí)間劃分成更小的粒度,當(dāng)更小粒度的時(shí)間間隔過(guò)去后,把過(guò)去的間隔請(qǐng)求數(shù)減掉,再補(bǔ)充一個(gè)空的時(shí)間間隔,需要的朋友可以參考下2022-07-07
Intellij IDEA實(shí)現(xiàn)SpringBoot項(xiàng)目多端口啟動(dòng)的兩種方法
有時(shí)候使用springboot項(xiàng)目時(shí)遇到這樣一種情況,用一個(gè)項(xiàng)目需要復(fù)制很多遍進(jìn)行測(cè)試,除了端口號(hào)不同以外,沒(méi)有任何不同。遇到這種情況怎么辦呢?這時(shí)候可以使用Intellij IDEA解決2018-06-06
SpringMVC之@requestBody的作用及說(shuō)明
這篇文章主要介紹了SpringMVC之@requestBody的作用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04

