SpringMVC中處理Http請求的原理詳解
SpingMVC處理Http請求原理
當(dāng)一個http請求過來了首先經(jīng)過的是DispatcherServlet這么一個前端控制器并調(diào)用了這個前端控制器的doService方法。
這個方法最終我們發(fā)現(xiàn)它調(diào)用了doDispatcher這么一個方法。這就是SpringMVC處理http請求的入口了。
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); } } }
這個方法里面我們發(fā)現(xiàn)定義了一個ModelAndView將要返回的視圖與數(shù)據(jù),并且首先檢測這個請求是不是一個上傳的請求,然后根據(jù)這個請求獲取對應(yīng)的Handler,如果Handler不為空的話就根據(jù)這個handler獲取對應(yīng)的HandlerAdapter。接著調(diào)用攔截器鏈的所有前置攔截的這么一個方法,接著調(diào)用adapter的真正的處理方法處理請求,最后調(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 { //定義一個空的ModelAndView ModelAndView err = null; Object dispatchException = null; try { //檢查是否是上傳請求 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語句省略。。。。。
繼續(xù)跟進(jìn)這個getHandler方法看看具體是怎么實現(xiàn)的,發(fā)現(xiàn)就是循環(huán)遍歷最開始初始化DispatcherServlet的時候初始化的那7個handlerMapping,去調(diào)用這些handlerMapping的getHandler方法;這里我們主要看這個RequestMappingHandlerMapping。
跟進(jìn)發(fā)現(xiàn)是調(diào)用了RequestMappingHandlerMapping的父類AbstractHandlerMapping中的getHandler方法,并且這個方法又是去調(diào)用了getHandlerInternal來獲取Handler的。
繼續(xù)跟進(jìn)這個getHandlerInternal方法,首先它根據(jù)這個request獲取它的URI,接著通過uri獲取對應(yīng)的HandlerMethod對象最終返回了。
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)這個getLookupPathForRequest發(fā)現(xiàn)又調(diào)用了getPathWithinServletMapping方法。
這里我們就知道了這個lookupPath得到的就是這個請求的URI,拿到了這個URI接著就去調(diào)用lookupHandlerMethod方法了。
這里lookupHandlerMethod方法顧名思義就是通過這個URI根據(jù)請求的URI去尋找對應(yīng)的HandlerMethod。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { ArrayList matches = new ArrayList(); //通過這個uri去Map中查詢與那個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); } //若查詢出這個uri有對應(yīng)的映射關(guān)系 if(!matches.isEmpty()) { AbstractHandlerMethodMapping.MatchComparator comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request)); //對映射關(guān)系排序 matches.sort(comparator); if(this.logger.isTraceEnabled()) { this.logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches); } //獲取排序后第一個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); } }
最終得到了這么一個包含了Handler與這個URI具體處理方法的一個HandlerMethod對象。
注意現(xiàn)在這個handler可能還沒有被創(chuàng)建出來或者說沒有得到,只是知道它的beanName。
最終還要調(diào)用這么一個createWithResolvedBean方法來得到。
獲得了這個handler之后,又繼續(xù)根據(jù)這個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這個方法最終所有通過這個URI返回了一個最開始注冊的handler,然后通過 這個handler返回了一個包含這這個HandlerMethod以及一個攔截器鏈的這么一個對象。之后執(zhí)行前置攔截器,handler方法,后置攔截,完成。
到此這篇關(guān)于SpingMVC中處理Http請求的原理詳解的文章就介紹到這了,更多相關(guān)SpingMVC處理Http請求內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringMVC 中HttpMessageConverter簡介和Http請求415 的問題
- springMVC中HttpMessageConverter的具體使用
- SpringMVC配置javaConfig及StringHttpMessageConverter示例
- SpringMVC HttpMessageConverter消息轉(zhuǎn)換器
- SpringMVC @RequestBody自動轉(zhuǎn)json Http415錯誤的解決
- SpringMVC HttpMessageConverter報文信息轉(zhuǎn)換器
- SpringMVC如何自定義響應(yīng)的HTTP狀態(tài)碼
相關(guān)文章
IntelliJ IDEA將導(dǎo)入的項目轉(zhuǎn)成maven項目
這篇文章主要介紹了IntelliJ IDEA將導(dǎo)入的項目轉(zhuǎn)成maven項目,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09spring boot @PathVariable傳遞帶反斜杠參數(shù) / 的處理
這篇文章主要介紹了spring boot @PathVariable傳遞帶反斜杠參數(shù) / 的處理操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02Java-Redis-Redisson分布式鎖的功能使用及實現(xiàn)
這篇文章主要介紹了Java-Redis-Redisson-分布式鎖的功能使用及實現(xiàn),本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-08-08Mybatis Order by動態(tài)參數(shù)防注入方式
這篇文章主要介紹了Mybatis Order by動態(tài)參數(shù)防注入方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04Java AOP實現(xiàn)自定義滑動窗口限流器方法詳解
這篇文章主要介紹了Java AOP實現(xiàn)自定義滑動窗口限流器方法,其中滑動窗口算法彌補了計數(shù)器算法的不足,滑動窗口算法把間隔時間劃分成更小的粒度,當(dāng)更小粒度的時間間隔過去后,把過去的間隔請求數(shù)減掉,再補充一個空的時間間隔,需要的朋友可以參考下2022-07-07Intellij IDEA實現(xiàn)SpringBoot項目多端口啟動的兩種方法
有時候使用springboot項目時遇到這樣一種情況,用一個項目需要復(fù)制很多遍進(jìn)行測試,除了端口號不同以外,沒有任何不同。遇到這種情況怎么辦呢?這時候可以使用Intellij IDEA解決2018-06-06