最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring的請求映射handlerMapping以及原理詳解

 更新時間:2023年08月31日 09:40:36   作者:結(jié)城明日奈是我老婆  
這篇文章主要介紹了Spring的請求映射handlerMapping以及原理詳解,我們每次發(fā)請求,它到底是怎么找到我們哪個方法來去處理這個請求,因為我們知道所有的請求過來都會來到DispatcherServlet,springboot底層還是使用的是springMVC,需要的朋友可以參考下

請求映射原理

也就是說我們每次發(fā)請求,它到底是怎么找到我們哪個方法來去處理這個請求,因為我們知道所有的請求過來都會來到 DispatcherServlet 。

springboot 底層還是使用的是 springMVC 所以 springMVC 的 DispatcherServlet 是處理所以請求的開始,他的整個請求處理方法是,我們來找一下:

DispatcherServlet 說起來也是一個 servlet 它繼承 FrameworkServlet 又繼承于 HttpServletBean 又繼承于 HttpServlet 。

說明

DispatcherServlet 是一個 HttpServlet ,繼承于 Servlet 必須重寫 doGet 或 doPost 之類的方法 Ctril + F12 (打開 HttpServlet 整個結(jié)構(gòu))我們發(fā)現(xiàn)這里沒有 doGet() 或 doSet() 方法,那說明子類里面有沒有重寫。

它的繼承樹是:

DispatcherServlet -> FrameworkServlet -> HttpServletBean -> HttpServlet

我們原生的 Servlet 本來有 doGet 和 doPost 方法,但是我們發(fā)現(xiàn)在 HttpServletBean 里面沒有找到,那就在 FrameworkServlet 里面有沒有, Ctril + F12 看看有沒有 doGet 和 doPost 。

protected final void doGet() 是繼承了 HttpServletBean 。

在 FrameworkServlet 里 我們發(fā)現(xiàn)無論是 doGet 還是 doPost 最終都是調(diào)用我們本類的 processRequest 說明我們請求處理一開始我們 HttpServlet 的 doGet 最終會調(diào)用到我們 FrameworkServlet 里面的 processRequest

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = this.buildLocaleContext(request);
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());
    this.initContextHolders(request, localeContext, requestAttributes);
    //這些都是初始化過程
    try {
        this.doService(request, response);
    } catch (IOException | ServletException var16) {
        failureCause = var16;
        throw var16;
    } catch (Throwable var17) {
        failureCause = var17;
        throw new NestedServletException("Request processing failed", var17);
    } finally {
        this.resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }
        this.logResult(request, response, (Throwable)failureCause, asyncManager);
        this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
    }
}

processRequest -> doService()

我們嘗試執(zhí)行一個 doService() 方法,執(zhí)行完后都是一些清理過程( catch )

protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;

它由于是一個抽象方法( abstract ),他也沒有重寫和實現(xiàn),只能來到子類( DispatcherServlet )來到 DispatcherServlet 來找 doService() :

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.logRequest(request);
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            Enumeration attrNames = request.getAttributeNames();
            label116:
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label116;
                    }
                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
                attributesSnapshot.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 inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }
            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }
        RequestPath previousRequestPath = null;
        if (this.parseRequestPath) {
            previousRequestPath = (RequestPath)request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
            ServletRequestPathUtils.parseAndCache(request);
        }
        try {
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }
            if (this.parseRequestPath) {
                ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
            }
        }
    }

也就是說最終 DispatcherServlet 里面對 doService() 進行了實現(xiàn)

只要看到 get 、 set 都是在里面放東西進行初始化的過程

我們一連串 請求一進來應(yīng)該是調(diào)HttpServlet的doGet,在FrameworkServlet重寫了 唯一有效語句是抽象類doService()方法,而這個方法在DispatcherServlet類中實現(xiàn)

核心的方法調(diào)用

try {
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }
            if (this.parseRequestPath) {
                ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
            }
        }

叫 doDispatch

finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }
            if (this.parseRequestPath) {
                ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
            }
        }

意思是把我們 doDispatch() 請求做派發(fā),看看 doDispatch() :

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 mv = null;//初始化數(shù)據(jù)
            Object dispatchException = null;//初始化數(shù)據(jù)
            try {
                processedRequest = this.checkMultipart(request);//檢查我們是否有文件上傳請求
                multipartRequestParsed = processedRequest != request;//如果是文件上傳請求它在這進行一個轉(zhuǎn)化
                // Determine handler for the current request.	就是我們來決定哪個handler(Controller)能處理當(dāng)前請求(current)
                mappedHandler = this.getHandler(processedRequest);
                if (mappedHandler == null) {
                    this.noHandlerFound(processedRequest, response);
                    return;
                }
                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                String method = request.getMethod();
                boolean isGet = HttpMethod.GET.matches(method);
                if (isGet || HttpMethod.HEAD.matches(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                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);
            }
            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);
        }
    }
}

我們發(fā)現(xiàn)這里才是真正有功能的方法

 processedRequest = this.checkMultipart(request);  //checkMultipart():檢查文件上傳

doDispatch() 才是我們 DispatcherServlet 里面最終要研究的方法,每一個請求進來都要調(diào)用 doDispatch 方法

斷點

我們打上斷點,來看整個請求處理, 包括 它是怎么找到我們每一個請求要調(diào)用誰來處理的

如果我來發(fā)送請求(登錄( localhost:8080 ))放行,知道頁面出來后我們點擊 REST-GET 請求,我們來看, protected void doDispatch(HttpServletRequest request, HttpServletResponse response) 傳入原生的 request 和 response ;我們點進 request 里面我們發(fā)現(xiàn)我們整個請求的路徑( coyoteRequest )是 /user ,請求的詳細(xì)信息都在這( coyoteRequest ),路徑( decodeUriMB )是 /user 。我們接下開看要用誰調(diào)用的。

HttpServletRequest processedRequest = request; 相當(dāng)于把原生的請求( request )拿過來包裝一下( processedRequest )。

這有個 HandlerExecutionChain mappedHandler = null; 執(zhí)行量我們后來再說

然后繼續(xù),說 multipartRequestParsed 是不是一個文件上傳請求,默認(rèn)是 false ,然后包括我們整個請求期間有沒有異步( getAsyncManager() ),如果有異步使用異步管理器( WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); )暫時在這塊我們不用管。

注意 :只有一個 ModelAndView mv = null;Object dispatchException = null; 這些都是空初始化的一些數(shù)據(jù)。加下來看我們第一個有功能的一塊:在 doDispatch() 里面第一個有功能的叫 checkMultipart (檢查我們是否有文件上傳請求,響應(yīng)文件上傳再說)如果是文件上傳請求它在這進行一個轉(zhuǎn)化( multipartRequestParsed = processedRequest != request; )注意這有一個 // Determine handler for the current request. , 就是我們來決定哪個handler能處理當(dāng)前請求(current) 我們放行( mappedHandler = this.getHandler(processedRequest); ) mappedHandler 就會看到:它直接給我們找到了 HelloCOntroller 的 getUser() 方法來處理這個請求

神奇的地方就在這個(mappedHandler = this.getHandler(processedRequest);) 它到底是怎么找到我當(dāng)前的 /User 請求會要調(diào)用那個方法進行處理的。

我們從 HttpServletRequest processedRequest = request; 直接放行到 mappedHandler = this.getHandler(processedRequest); , getHandler 他要依據(jù)當(dāng)前請求( processedRequest )當(dāng)前請求里面肯定有哪個 url 地址這是 http 傳過來的不用管( Step into )進來,這里有一個東西叫 handlerMappings 也就是獲取到所有的(這個 handlerMappings 有五個)

1、handlerMapping:處理器映射

也就是說我們springMVC怎么知道哪個請求要用誰處理,是根據(jù)處理器里面的映射規(guī)則。

也就是說:

/xxx請求 -> xxx處理 都有這映射規(guī)則,而這些規(guī)則都被保存到 handlerMapping 里面,如上圖所示,我們現(xiàn)在有5個 handlerMapping ,其中有幾個 handlerMapping 大家可能有點熟悉,比如: WelcomePageHandlerMapping (歡迎頁的處理請求),我們之前說資源管理規(guī)則的時候我們發(fā)現(xiàn)我們springMVC自動的會給容器中放一個歡迎頁的 handlerMapping 然后這個 handlerMapping ()里面也有保存規(guī)則,保存什么規(guī)則?就是我們所有的 index 請求你的比如這個 PathMatcher (路徑匹配)我們這個 / (當(dāng)前項目下的’/‘你直接訪問這個)我給你訪問到哪?我們的’/‘會直接 rootHandler下的View路徑 (這里是等于到了’index’)所以我們首頁要訪問到的是我們這個 WelcomePageHandlerMapping 里面保存了一個規(guī)則,所以就有首頁的訪問了。

我們加下來還有一個 RequestMappingHandlerMapping

2、RequestMappingHandlerMapping

我們以前有一個注解叫 @RequestMapping 相當(dāng)于是 @RequestMapping 注解的所有處理器映射,也就是說這個東西( RequestMappingHandlerMapping )里面保存了所有 @RequestMapping 和 handler 的規(guī)則。而它又是怎么保存的,那其實是 我們的應(yīng)用一啟動springMVC自動掃描我們所有的 Controller 并解析注解,把你的這些注解信息全部保存到HandlerMapping里面 。所以它會在這五個 handlerMapping 里面(大家注意增強for循環(huán))挨個找我們所有的請求映射,看誰能處理這個請求,我們找到第一個 handlerMapping 相當(dāng)于我們的 RequestMappingHandlerMapping ,它里面保存了哪些映射信息,有個 mappingRegistry (相當(dāng)于我們映射的注冊中心)這個中心里面打開你就會發(fā)現(xiàn)

我們當(dāng)前項目里寫的所有的路徑它在這個都有映射: POST[/user] 是哪個Controller哪個方法處理的,包括系統(tǒng)自帶的 /error 它是哪個 Controller 哪個方法處理的

相當(dāng)于是我們 RequestMappingHandlerMapping 保存了我們當(dāng)前系統(tǒng)我們每一個自己寫的類,每一個類每一個方法都能處理什么請求。我們當(dāng)前請求 /user ,我們遍歷到第一個 HandlerMapping 的時候相當(dāng)于它的注冊中心( mappingRegistry )里面就能找到 /user 是誰來處理,所以最終在這決定是在 HelloController#getUser 來處理的。所以它在這( HandlerExecutionChain handler = mapping.getHandler(request); )所以它在 mapping 里面 getHandler ( mapping.getHandler );從我們的 HandlerMapping 里面獲取( getHandler() )我們的 handler 就是處理器,點進 getHandler()

@Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
		// Ensure presence of cached lookupPath for interceptors and others
		if (!ServletRequestPathUtils.hasCachedPath(request)) {
			initLookupPath(request);
		}
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		if (logger.isTraceEnabled()) {
			logger.trace("Mapped to " + handler);
		}
		else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
			logger.debug("Mapped to " + executionChain.getHandler());
		}
		if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
			CorsConfiguration config = getCorsConfiguration(handler, request);
			if (getCorsConfigurationSource() != null) {
				CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
				config = (globalConfig != null ? globalConfig.combine(config) : config);
			}
			if (config != null) {
				config.validateAllowCredentials();
			}
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		return executionChain;
	}

getHandlerInterna() 我們來獲取,怎么獲取( step into ):

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
		try {
			return super.getHandlerInternal(request);
		}
		finally {
			ProducesRequestCondition.clearMediaTypesAttribute(request);
		}
	}

( step into ) getHandlerInternal() :

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = initLookupPath(request);
		this.mappingRegistry.acquireReadLock();//拿到一把鎖
		try {
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

它先拿到 request 原生請求,我們現(xiàn)在想要訪問的路徑( lookupPath )我們想要訪問的路徑是 /user 然后帶著這個路徑,它還拿到一把鎖( acquireReadLock() )害怕我們并發(fā)查詢我們這個 mappingRegistry 這個 mappingRegistry 也看到了是我們這個 RequestMappingHandlerMapping , handlerMapping 里面的一個屬性 mappingRegistry 它里面保存了我們所有請求調(diào)用哪個方法處理

所以它最終相當(dāng)于是在我們當(dāng)前請求( request )這個路徑( lookupPath )到底誰來處理( handlerMethod )

(step into):

@Nullable
	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {//沒找到
			addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);//添加一些空的東西
		}
		if (!matches.isEmpty()) {
			Match bestMatch = matches.get(0);//它把它找到的你里面的第一個拿過來
			if (matches.size() > 1) {
				Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
				matches.sort(comparator);
				bestMatch = matches.get(0);
				if (logger.isTraceEnabled()) {
					logger.trace(matches.size() + " matching mappings: " + matches);
				}
				if (CorsUtils.isPreFlightRequest(request)) {
					for (Match match : matches) {
						if (match.hasCorsConfig()) {
							return PREFLIGHT_AMBIGUOUS_MATCH;
						}
					}
				}
				else {
					Match secondBestMatch = matches.get(1);
					if (comparator.compare(bestMatch, secondBestMatch) == 0) {
						Method m1 = bestMatch.getHandlerMethod().getMethod();
						Method m2 = secondBestMatch.getHandlerMethod().getMethod();
						String uri = request.getRequestURI();
						throw new IllegalStateException(
								"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
					}
				}
			}
			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.getHandlerMethod();
		}
		else {
			return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
		}
	}

形參:

lookupPath(當(dāng)前我們要找的路徑);request(我們原生的請求),接下來就在下面開始找。

從我們這個 Registry ( mappingRegistry )里面,使用我們這個路徑( getMappingsByDirectPath(lookupPath) )然后去找誰能處理;

問題就是在于我們這個 Registry ( RequestMappingHandlerMapping )里面他的路徑光靠 /user 請求其實有四個人的路徑都是這樣只是請求方式不對。

getMappingsByDirectPath()

先是根據(jù) url ( getMappingsByDirectPath() 視頻里是 getMappingByUrl() )來找,按照前面的來能找到4個( directPathMatches (+ArraysList@7307 size=4) )( GET 、 POST 、 PUT 、 DELETE 方式的user)找到了以后接下來它把所有找到的添加到我們這個匹配的集合里面( addMatchingMappings(directPathMatches, matches, request); )

如果沒找到( if (matches.isEmpty()) )它就添加一些空的東西( addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request); ),如果找到了還不為空( if (!matches.isEmpty()) )接下來它把它找到的你里面的第一個拿過來( Match bestMatch = matches.get(0); )

如果它同時找到了很多他就認(rèn)為第一個是最匹配的,而且大家注意: 如果我們現(xiàn)在的 matches.size() 大于1( if (matches.size() > 1) )也就是相當(dāng)于我們找到了非常多的 matches ;

注意:這個 matches 在這( addMatchingMappings(directPathMatches, matches, request); ),在這一塊( directPathMatches )找到了四個,它( matches )把這四個調(diào)用 addMatchingMappings() 這個方法獲取到能匹配的集合里面( matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping))); ); addMatchingMappings() 肯定以請求方式匹配好了

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
		for (T mapping : mappings) {
			T match = getMatchingMapping(mapping, request);
			if (match != null) {
				matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
			}
		}
	}

所以最終給我們留下我們最佳匹配的這個 matches 里面集合里面只有一個,他( bestMatch = matches.get(0); )就拿到這個,所以最終給我們留下一個我們最佳匹配的我們這個 matches (里面只有一個),然后它( matches.get(0); )就拿到這個( matches ),這個 matches ( matches.get(0); )已經(jīng)得到最佳匹配的了,如果你寫了多個方法同時都能處理 /GET 請求,那你的這個 matches 就能大于1( if (matches.size() > 1) )大于1后(if里面)各種排序排完以后在這( Match secondBestMatch = matches.get(1); )最后給你測試,把你能匹配的一倆個全都拿來進行對比

最終比完后給你抱一個錯說: throw new IllegalStateException( "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); (相當(dāng)于我們這個 handler 你能處理這個 uri (路徑) 有倆個方法都能處理)說明就會拋出異常( IllegalStateException ),所以 springMVC 要求我們:同樣的一個請求方式不能有多個方法同時能處理,只能有一個。

springMVC 要求我們:同樣的一個請求方式不能有多個方法同時能處理,只能有一個(原因在上面一段)

最終我們就找到我們最佳匹配規(guī)則(GET方式的user)能匹配,而GET方式的User是 Controller#getUser() 方法

所以簡單總結(jié)起來就是一句話:怎么知道哪個請求誰能處理?所有的請求映射都保存在了``HandlerMapping`中。

我們 springboot 一啟動給我們配置了 welcomePageHandlerMapping (歡迎頁的 handlerMapping )

  • springboot自動配置歡迎頁的handlerMaping。訪問’/'能訪問到’index’頁面
  • 請求進來,挨個嘗試所有的HandlerMapping看是否有請求信息。
    • 如果有就找個請求對象的handler
    • 如果沒有就是下一個HandlerMapping

總結(jié)

(這里以歡迎頁為例) localhost:8080/ 進入到首頁,我們請求一進來( HttpServletRequest processedRequest = request; )它來找( mappedHandler = getHandler(processedRequest); (當(dāng)前請求訪問的是 / 的請求))看誰能處理( getHandler() )接下來就進入了遍歷循環(huán)( forEach )所有 HandlerMapping 的時候了,我們先來找到第一個 handlerMapping ( HandlerExecutionChain handler = mapping.getHandler(request); )第一個 HandlerMapping 由于我們這個里面映射只保存了相當(dāng)于是我們自己 Controller 寫的這個路徑映射沒人能處理,所以如果在第一個里面找,你找到的handler肯定是空的,找到是空的,所以繼續(xù)for循環(huán);再次for循環(huán)來到第二個 handlerMapping 叫 WelcomePageHandlerMapping ,它正好處理的路徑就是’/'所以我們現(xiàn)在找,就找到 handler 了,而這個 handler 是什么?就是我們 springMVC 里面默認(rèn)處理我們’index’頁面,人家給我們的 ViewController 訪問我們的’index’頁面就有人了。這就是 handlerMapping 的匹配規(guī)則。所有的 handleMapping 全部來進行匹配誰能匹配用誰的 springboot幫我們配置了哪些HandlerMapping

我們來到webMvcAutoCOnfig看看有沒有跟HandlerMapping有關(guān)的:

首先第一個:

  • RequestMappingHandlerMapping
    • 相當(dāng)于我們?nèi)萜髦?@Bean),我們第一個HandlerMaping是我們springboot給我們?nèi)萜鞣诺哪J(rèn)的。 這個組件就是來解析我們當(dāng)前所有的方法(Controller里的方法)標(biāo)了@RequestMapping注解(GET PUT都一樣)標(biāo)了這些注解的時候它(RequestMappingHandlerMapping)整的。
  • WelcomePageHandlerMapping
    • 歡迎頁的HandlerMapping
  • 還有我們系統(tǒng)兼容的BeanNameUrlHandlerMapping、RouterFunctionMapping和SimpleUrlHandlerMapping
  • 一句話: 我們需要自定義的映射處理,我們也可以自己在容器中放HandlerMapping(就是來保存一個請求誰來處理,甚至于是發(fā)一個(我們經(jīng)常自定義handlerMappingapi/v1/user和api/v2/user(v2版本獲取用戶)不一樣,v1版本調(diào)用哪個v2版本調(diào)用哪個)這就可能不止止是Controller的變化,我們希望能自定義handlerMapping的規(guī)則。如果是v1版本,所有的請求,比如去哪個包里找;如果是v2版本給我去哪個包里找。這樣就會非常方便)自定義handlerMapping

到此這篇關(guān)于Spring的請求映射handlerMapping以及原理詳解的文章就介紹到這了,更多相關(guān)Spring的請求映射handlerMapping內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于java file 文件操作operate file of java的應(yīng)用

    基于java file 文件操作operate file of java的應(yīng)用

    本篇文章介紹了,基于java file 文件操作operate file of java的應(yīng)用。需要的朋友參考下
    2013-05-05
  • 多線程下的事務(wù)失效及解決方案

    多線程下的事務(wù)失效及解決方案

    這篇文章主要介紹了多線程下的事務(wù)失效及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • 最新springboot解決跨域的幾種方式小結(jié)

    最新springboot解決跨域的幾種方式小結(jié)

    跨域指的是瀏覽器不能執(zhí)?其他?站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對javascript施加的安全限制,這篇文章主要介紹了springboot解決跨域的幾種方式,需要的朋友可以參考下
    2022-05-05
  • java圖片識別文字的方法

    java圖片識別文字的方法

    這篇文章主要為大家詳細(xì)介紹了java圖片識別文字的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • Java中JSONObject與JSONArray的使用區(qū)別詳解

    Java中JSONObject與JSONArray的使用區(qū)別詳解

    這篇文章主要介紹了Java中JSONObject與JSONArray的使用區(qū)別詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • Java合并集合幾種常見方式總結(jié)(List、Set、Map)

    Java合并集合幾種常見方式總結(jié)(List、Set、Map)

    這篇文章主要介紹了Java中合并List、Set、Map的多種方法,包括addAll()、Stream.concat()、Stream.of()+flatMap()、List.copyOf()、putAll()、merge()、compute()和StreamAPI等,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-03-03
  • Java函數(shù)接口和Lambda表達式深入分析

    Java函數(shù)接口和Lambda表達式深入分析

    這篇文章主要介紹了Java函數(shù)接口和Lambda表達式,函數(shù)接口是一個具有單個抽象方法的接口,接口設(shè)計主要是為了支持Lambda表達式和方法引用,使得Java能更方便地實現(xiàn)函數(shù)式編程風(fēng)格,需要的朋友可以參考下
    2025-04-04
  • Java數(shù)據(jù)結(jié)構(gòu)之樹和二叉樹的相關(guān)資料

    Java數(shù)據(jù)結(jié)構(gòu)之樹和二叉樹的相關(guān)資料

    這篇文章主要介紹了Java?數(shù)據(jù)結(jié)構(gòu)之樹和二叉樹相關(guān)資料,文中通過示例代碼和一些相關(guān)題目來做介紹,非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下!
    2023-01-01
  • 詳解Java 線程中斷

    詳解Java 線程中斷

    這篇文章主要介紹了Java 線程中斷的相關(guān)資料,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2021-02-02
  • 不同Java泛型構(gòu)造函數(shù)的詳解

    不同Java泛型構(gòu)造函數(shù)的詳解

    這篇文章主要介紹了不同Java泛型構(gòu)造函數(shù)的詳解,因為對象是應(yīng)用類型,對象賦值是指向同一個對象,所以如果需要保存對象某個時刻的狀態(tài),就需要構(gòu)造函數(shù)來new一個新的對象。下面我們來詳細(xì)了解一下吧
    2019-06-06

最新評論

安顺市| 门源| 敦化市| 新和县| 江安县| 溧水县| 买车| 望奎县| 南雄市| 台东市| 宁津县| 通渭县| 深泽县| 略阳县| 林西县| 镇宁| 青田县| 正蓝旗| 石柱| 阳城县| 娱乐| 尚义县| 武隆县| 新泰市| 天长市| 图片| 桃江县| 宜阳县| 娄底市| 广丰县| 通州区| 遂川县| 大足县| 门头沟区| 许昌县| 乐亭县| 修水县| 乐昌市| 武功县| 南安市| 蕉岭县|