亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解SpringMVC組件之HandlerMapping(一)

 更新時(shí)間:2023年08月31日 10:57:21   作者:姠惢荇者  
這篇文章主要介紹了詳解SpringMVC組件之HandlerMapping(一),HandlerMapping組件是Spring?MVC核心組件,用來(lái)根據(jù)請(qǐng)求的request查找對(duì)應(yīng)的Handler,在Spring?MVC中,有各式各樣的Web請(qǐng)求,每個(gè)請(qǐng)求都需要一個(gè)對(duì)應(yīng)的Handler來(lái)處理,需要的朋友可以參考下

1、HandlerMapping簡(jiǎn)介

HandlerMapping組件是Spring MVC核心組件,用來(lái)根據(jù)請(qǐng)求的request查找對(duì)應(yīng)的Handler。

在Spring MVC中,有各式各樣的Web請(qǐng)求,每個(gè)請(qǐng)求都需要一個(gè)對(duì)應(yīng)的Handler來(lái)處理,具體接收到一個(gè)request請(qǐng)求,應(yīng)該有那個(gè)Handler處理呢?這就是HandlerMapping組件的作用。

在Spring MVC框架中,HandlerMapping組件及其實(shí)現(xiàn)類(lèi)的如下所示:

在這里插入圖片描述

在HandlerMapping類(lèi)的層級(jí)結(jié)構(gòu)圖中,MatchableHandlerMapping接口是一個(gè)從Spring4.3.1開(kāi)始新增的一個(gè)接口,用來(lái)判斷給定的請(qǐng)求是否符合請(qǐng)求條件。

除此之外,HandlerMapping接口有一個(gè)公共的抽象類(lèi)AbstractHandlerMapping,所有子孫實(shí)現(xiàn)類(lèi)都需要繼承。

該抽象類(lèi)下有三個(gè)直接子類(lèi),分別是AbstractHandlerMethodMapping、AbstractUrlHandlerMapping和RouterFunctionMapping

其中RouterFunctionMapping是從Spring MVC5.2開(kāi)始引入的,主要用于WebFlux處理中;

而另外兩個(gè)直接實(shí)現(xiàn)類(lèi),代表了兩大類(lèi)實(shí)現(xiàn)方式:

  • AbstractUrlHandlerMapping表示根據(jù)url獲取對(duì)應(yīng)的handler;
  • AbstractHandlerMethodMapping表示基于方法的映射方式,這也是我們?cè)趯?shí)際工作中使用較多的一種方式。

2、AbstractHandlerMapping抽象類(lèi)

在這里插入圖片描述

AbstractHandlerMapping是HandlerMapping的抽象類(lèi),所有子類(lèi)都是繼承于該抽象類(lèi)。該抽象類(lèi)采用了模板方法,定義了HandlerMapping的核心邏輯。

在抽象類(lèi)AbstractHandlerMapping中,通過(guò)繼承WebApplicationObjectSupport類(lèi)(間接實(shí)現(xiàn)了ApplicationContextAware接口),實(shí)現(xiàn)了攔截器相關(guān)信息的初始化,然后實(shí)現(xiàn)了接口中的getHandler()方法,通過(guò)調(diào)用抽象方法getHandlerInternal()獲取Handler,然后把Handler封裝成HandlerExecutionChain對(duì)象(該對(duì)象包括了攔截器信息和跨域訪問(wèn)相關(guān)信息),并進(jìn)行返回。

攔截器初始化:

首先分析,AbstractHandlerMapping抽象類(lèi)如何初始化攔截器的。因?yàn)锳bstractHandlerMapping抽象類(lèi)間接繼承了ApplicationContextAware接口,所以容器初始化是會(huì)自動(dòng)調(diào)用setApplicationContext()方法,該setApplicationContext()方法經(jīng)過(guò)ApplicationObjectSupport、WebApplicationObjectSupport層級(jí),最終調(diào)用了AbstractHandlerMapping抽象類(lèi)的initApplicationContext()方法,攔截器的初始化工作就是在這個(gè)方法中實(shí)現(xiàn)的,代碼如下:

@Override
protected void initApplicationContext() throws BeansException {
	extendInterceptors(this.interceptors);
	detectMappedInterceptors(this.adaptedInterceptors);
	initInterceptors();
}

其中,extendInterceptors()方法是模板方法,供子類(lèi)重寫(xiě),提供添加或修改攔截器的入口;detectMappedInterceptors()方法,用于將容器(包括父級(jí)容器)中所有注冊(cè)的MappedInterceptor類(lèi)型的Bean添加到adaptedInterceptors屬性中;initInterceptors()方法用于初始化攔截器。

detectMappedInterceptors()方法 通過(guò)BeanFactoryUtils.beansOfTypeIncludingAncestors()方法查詢(xún)所有的MappedInterceptor類(lèi)型的實(shí)例,并添加到adaptedInterceptors屬性中。

protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
	mappedInterceptors.addAll(
			BeanFactoryUtils.beansOfTypeIncludingAncestors(
					obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}

initInterceptors()方法 在初始化方法中,主要實(shí)現(xiàn)了把interceptors屬性中的攔截器根據(jù)攔截器類(lèi)型,進(jìn)行適配,然后放到adaptedInterceptors變量中。

在Spring4.2之前的版本,有一個(gè)變量mappedInterceptors ,所以MappedInterceptor類(lèi)型的攔截器會(huì)放到該變量中,但是再Spring4.2及之后的版本,該變量被移除,所以interceptors屬性中的MappedInterceptor類(lèi)型(HandlerInterceptor的子類(lèi))的實(shí)例,進(jìn)行適配時(shí)按照HandlerInterceptor類(lèi)型進(jìn)行,并統(tǒng)一保存到了adaptedInterceptors變量,不再進(jìn)行區(qū)分。

protected void initInterceptors() {
	if (!this.interceptors.isEmpty()) {
		for (int i = 0; i < this.interceptors.size(); i++) {
			Object interceptor = this.interceptors.get(i);
			if (interceptor == null) {
				throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
			}
			this.adaptedInterceptors.add(adaptInterceptor(interceptor));
		}
	}
}
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
	if (interceptor instanceof HandlerInterceptor) {
		return (HandlerInterceptor) interceptor;
	}
	else if (interceptor instanceof WebRequestInterceptor) {
		return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
	}
	else {
		throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
	}
}

getHandler()方法實(shí)現(xiàn)

在HandlerMapping接口中,通過(guò)getHandler()方法獲取request對(duì)應(yīng)的處理器Handler和攔截器Interceptor的,在AbstractHandlerMapping抽象類(lèi)的實(shí)現(xiàn)如下:

@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//調(diào)用模板方法,獲取request對(duì)應(yīng)的處理器Handler
	Object handler = getHandlerInternal(request);
	if (handler == null) {//如果handler為空,獲取默認(rèn)的handler
		handler = getDefaultHandler();
	}
	if (handler == null) {
		return null;
	}
	// 如果handler是實(shí)例的名稱(chēng),需要從容器中獲取對(duì)應(yīng)的Bean實(shí)例
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = obtainApplicationContext().getBean(handlerName);
	}
	//根據(jù)Handler獲取對(duì)應(yīng)的executionChain對(duì)象,該過(guò)程會(huì)把對(duì)應(yīng)的攔截器添加到executionChain對(duì)象中
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
	if (logger.isTraceEnabled()) {
		logger.trace("Mapped to " + handler);
	}
	else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
		logger.debug("Mapped to " + executionChain.getHandler());
	}
	//處理跨域問(wèn)題
	if (hasCorsConfigurationSource(handler)) {
		//初始化時(shí)如果有跨域配置,則獲取config 對(duì)象
		CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
		//如果當(dāng)前handler中有跨域配置,獲取handlerConfig對(duì)象
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		//合并配置
		config = (config != null ? config.combine(handlerConfig) : handlerConfig);
		//把跨域處理的攔截器,添加到HandlerExecutionChain對(duì)象中
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}
	return executionChain;
}

在getHandler()方法中,主要做了以下幾件事:

  1. 調(diào)用模板方法getHandlerInternal(),獲取request對(duì)應(yīng)的handler。模板方法由子類(lèi)實(shí)現(xiàn)。
  2. 當(dāng)獲取的handler為空時(shí),獲取默認(rèn)的handler,即獲取對(duì)應(yīng)的defaultHandler屬性
  3. 判斷handler是否時(shí)Bean的名稱(chēng),如果是,獲取對(duì)應(yīng)的Bean實(shí)例
  4. 獲取對(duì)應(yīng)的HandlerExecutionChain對(duì)象,封裝了初始化時(shí)的攔截器
  5. 處理跨域問(wèn)題,處理跨域問(wèn)題涉及到了內(nèi)部類(lèi)PreFlightHandler、CorsInterceptor和屬性CorsProcessor對(duì)應(yīng)的實(shí)現(xiàn)類(lèi),這里暫不深入分析。

3、AbstractUrlHandlerMapping類(lèi)

AbstractUrlHandlerMapping系列的類(lèi)都繼承自AbstractUrlHandlerMapping抽象類(lèi),主要用來(lái)通過(guò)URL進(jìn)行匹配。思路如下:把URL與Handler的對(duì)應(yīng)關(guān)系存到一個(gè)Map中,然后在getHandlerInternal方法中,根據(jù)URL去獲取對(duì)應(yīng)的Handler對(duì)象,在AbstractUrlHandlerMapping抽象類(lèi)中,主要實(shí)現(xiàn)了根據(jù)url獲取對(duì)應(yīng)Handler的方法,如何初始化這個(gè)Map對(duì)象,交由子類(lèi)進(jìn)行實(shí)現(xiàn)。

3.1、定義的屬性

//根處理器,處理“/”的處理器
@Nullable
private Object rootHandler;
//是否匹配尾部的“/”,比如:如果設(shè)置為ture,則"/users"的匹配模式,也會(huì)匹配"/users/"
private boolean useTrailingSlashMatch = false;
//設(shè)置是否延遲加載,只對(duì)單例的處理器有效
private boolean lazyInitHandlers = false;
//保存request和Handler對(duì)應(yīng)關(guān)系的變量
private final Map<String, Object> handlerMap = new LinkedHashMap<>();

3.2、getHandlerInternal()方法

實(shí)現(xiàn)了父類(lèi)中的抽象方法,根據(jù)request獲取對(duì)應(yīng)的handler,實(shí)際上還有由定義的lookupHandler()方法實(shí)現(xiàn)。如果沒(méi)有獲取對(duì)應(yīng)的handler,就會(huì)嘗試獲取根處理器或默認(rèn)處理器。

@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
	//獲取lookupPath,并保存到request屬性中
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	request.setAttribute(LOOKUP_PATH, lookupPath);
	//獲取lookupPath 對(duì)應(yīng)的handler
	Object handler = lookupHandler(lookupPath, request);
	if (handler == null) {//如果沒(méi)有獲取到對(duì)應(yīng)的handler,則進(jìn)行下面處理
		// We need to care for the default handler directly, since we need to
		// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
		Object rawHandler = null;
		if ("/".equals(lookupPath)) {//如果時(shí)根路徑,則獲取根處理器,即屬性rootHandler中保存的處理器
			rawHandler = getRootHandler();
		}
		if (rawHandler == null) {//獲取默認(rèn)處理器,在父類(lèi)中定義,即父類(lèi)中的defaultHandler屬性
			rawHandler = getDefaultHandler();
		}
		if (rawHandler != null) {
			// Bean name or resolved handler?
			if (rawHandler instanceof String) {//獲取對(duì)應(yīng)的Bean實(shí)例
				String handlerName = (String) rawHandler;
				rawHandler = obtainApplicationContext().getBean(handlerName);
			}
			//模板方法,校驗(yàn)處理器,交由子類(lèi)實(shí)現(xiàn)或擴(kuò)展
			validateHandler(rawHandler, request);
			//根據(jù)原始的handler構(gòu)建實(shí)際的handler,主要實(shí)現(xiàn)構(gòu)建HandlerExecutionChain對(duì)象,并在request添加對(duì)應(yīng)的參數(shù),后續(xù)在詳細(xì)分析
			handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
		}
	}
	return handler;
}

在代碼上,添加了注釋?zhuān)辉俜治鎏幚磉壿?,我們下面詳?xì)分析其中的lookupHandler()方法和buildPathExposingHandler()方法。

3.2.1、lookupHandler()方法

因?yàn)閺腗ap對(duì)象獲取對(duì)應(yīng)的Handler,不是簡(jiǎn)單的Map.get(),因?yàn)檫€涉及到了正則匹配等問(wèn)題,所以在專(zhuān)門(mén)的方法中進(jìn)行處理,在該方法中,主要實(shí)現(xiàn)了根據(jù)lookupPath獲取對(duì)應(yīng)的handler,然后處理當(dāng)獲取多個(gè)匹配handler后,如何獲取最佳匹配的Handler等。

代碼如下:

@Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
	// urlPath正好對(duì)應(yīng)Map的key,則直接獲取,并進(jìn)行處理
	Object handler = this.handlerMap.get(urlPath);
	if (handler != null) {
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
		validateHandler(handler, request);
		return buildPathExposingHandler(handler, urlPath, urlPath, null);
	}
	// 模式匹配,獲取handlerMap中對(duì)應(yīng)的所有可能匹配的模式
	List<String> matchingPatterns = new ArrayList<>();
	for (String registeredPattern : this.handlerMap.keySet()) {
		if (getPathMatcher().match(registeredPattern, urlPath)) {
			matchingPatterns.add(registeredPattern);
		}
		else if (useTrailingSlashMatch()) {
			if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
				matchingPatterns.add(registeredPattern + "/");
			}
		}
	}
	String bestMatch = null;
	//定義最佳匹配的比較器,為了獲取最佳的處理器Handler
	Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
	if (!matchingPatterns.isEmpty()) {
		//根據(jù)比較器進(jìn)行排序,最佳handler排在第一個(gè)
		matchingPatterns.sort(patternComparator);
		if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
			logger.trace("Matching patterns " + matchingPatterns);
		}
		//獲取最佳的handler
		bestMatch = matchingPatterns.get(0);
	}
	if (bestMatch != null) {//獲取最佳處理器對(duì)應(yīng)的key,然后進(jìn)一步處理Handler
		//獲取key對(duì)應(yīng)的Handler
		handler = this.handlerMap.get(bestMatch);
		if (handler == null) {//如果沒(méi)有獲取到,則移除尾部的斜桿繼續(xù)嘗試
			if (bestMatch.endsWith("/")) {
				handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
			}
			if (handler == null) {//還是獲取不到,直接拋出異常
				throw new IllegalStateException(
						"Could not find handler for best pattern match [" + bestMatch + "]");
			}
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {//如果時(shí)bean的名稱(chēng),則獲取對(duì)應(yīng)的實(shí)例
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
		validateHandler(handler, request);
		String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
		//最佳匹配可能存在多個(gè),比如:/book/{id}和/book/{name},那就再添加一個(gè)UriTemplateVariablesHandlerInterceptor攔截器,并在request設(shè)置一個(gè)key為 org.springframework.web.servlet.HandlerMapping.uriTemplateVariables的變量。
		Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
		for (String matchingPattern : matchingPatterns) {
			if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
				Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
				Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
				uriTemplateVariables.putAll(decodedVars);
			}
		}
		if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
			logger.trace("URI variables " + uriTemplateVariables);
		}
		return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
	}
	// No handler found...
	return null;
}

3.2.2、buildPathExposingHandler()方法

在Map對(duì)象中映射的是真正的handler對(duì)象,在buildPathExposingHandler()方法中,實(shí)現(xiàn)了把rawHandler封裝成HandlerExecutionChain對(duì)象,然后添加內(nèi)部攔截器類(lèi)PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor。

protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
	String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {
	HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
	chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
	if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
		chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
	}
	return chain;
}

其中,PathExposingHandlerInterceptor攔截器主要設(shè)置了request中的 “xxx.bestMatchingHandler”、".introspectTypeLevelMapping"(一直為false)、.bestMatchingPattern"和".pathWithinHandlerMapping"屬性,UriTemplateVariablesHandlerInterceptor攔截器,主要是當(dāng)uriTemplateVariables不為空時(shí)設(shè)置,主要添加了"xxx.uriTemplateVariables"屬性,值就是變量uriTemplateVariables。其中,xxx表示HandlerMapping.class.getName(),即為“org.springframework.web.servlet.HandlerMapping”。

3.3、handlerMap的初始化

在AbstractUrlHandlerMapping抽象類(lèi)中,handlerMap(request和handler映射關(guān)系)的初始化主要由registerHandler()方法來(lái)實(shí)現(xiàn)的。而registerHandler()方法,一般在子類(lèi)中進(jìn)行調(diào)用,從而實(shí)現(xiàn)不同的子類(lèi)就可以通過(guò)注冊(cè)不同的Handler將組件創(chuàng)建出來(lái)。

//第一個(gè)方法:該重載方法,循環(huán)調(diào)用第二種實(shí)現(xiàn)真正的處理器注冊(cè)
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
	Assert.notNull(urlPaths, "URL path array must not be null");
	for (String urlPath : urlPaths) {
		registerHandler(urlPath, beanName);
	}
}
//第二個(gè)方法:真正實(shí)現(xiàn)處理器的注冊(cè)
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
	Assert.notNull(urlPath, "URL path must not be null");
	Assert.notNull(handler, "Handler object must not be null");
	Object resolvedHandler = handler;
	// 加載處理器(當(dāng)處理器是非延時(shí)加載時(shí)進(jìn)行)
	if (!this.lazyInitHandlers && handler instanceof String) {
		String handlerName = (String) handler;
		ApplicationContext applicationContext = obtainApplicationContext();
		if (applicationContext.isSingleton(handlerName)) {
			resolvedHandler = applicationContext.getBean(handlerName);
		}
	}
	Object mappedHandler = this.handlerMap.get(urlPath);
	if (mappedHandler != null) {//如果Map中存在處理器,且和當(dāng)前的不一樣,就直接拋出異常。
		if (mappedHandler != resolvedHandler) {
			throw new IllegalStateException(
					"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
					"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
		}
	}
	else {//當(dāng)前Map不存在處理器
		if (urlPath.equals("/")) {//設(shè)置根處理器
			if (logger.isTraceEnabled()) {
				logger.trace("Root mapping to " + getHandlerDescription(handler));
			}
			setRootHandler(resolvedHandler);
		}
		else if (urlPath.equals("/*")) {//設(shè)置默認(rèn)處理器
			if (logger.isTraceEnabled()) {
				logger.trace("Default mapping to " + getHandlerDescription(handler));
			}
			setDefaultHandler(resolvedHandler);
		}
		else {//在Map中添加urlPath和處理器對(duì)應(yīng)的關(guān)系
			this.handlerMap.put(urlPath, resolvedHandler);
			if (logger.isTraceEnabled()) {
				logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
			}
		}
	}
}

4、BeanNameUrlHandlerMapping類(lèi)

4.1、使用方法

1、定義一個(gè)Controller

public class TestController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)   {
        ModelAndView mav = new ModelAndView("test");
        mav.addObject("context", “test”);
        return mav;
    }
}

2、TestController 注入到Spring容器

@Configuration
public class ControllerBeanConfig {
    /**
     * 注意 :
     * 1. 該 bean 實(shí)現(xiàn)了接口 org.springframework.web.servlet.mvc.Controller,
     * 2. 該 bean 沒(méi)有使用注解 @Controller,
     * (如果使用了注解@Controller,就會(huì)被RequestMappingHandlerMapping接管,而不是由BeanNameUrlHandlerMapping處理)
     * 3. 映射到匹配 /test/* 的url
     * @return
     */
    @Bean(name = "/test/*")
    public TestController beanTestController() {
        return new TestController();
    }
}

通過(guò)上述配置,在初始化時(shí),會(huì)采用BeanNameUrlHandlerMapping類(lèi),進(jìn)行獲取request對(duì)應(yīng)de 處理器。在這個(gè)過(guò)程中,BeanNameUrlHandlerMapping類(lèi)是如何產(chǎn)生作用的呢?下面我們?cè)敿?xì)的分析。

4.2、AbstractDetectingUrlHandlerMapping、BeanNameUrlHandlerMapping原理

BeanNameUrlHandlerMapping類(lèi)繼承了抽象類(lèi)AbstractDetectingUrlHandlerMapping,然后又繼承了AbstractUrlHandlerMapping抽象類(lèi),AbstractUrlHandlerMapping抽象類(lèi)在前面已經(jīng)分析過(guò)了。在前面介紹抽象類(lèi)AbstractUrlHandlerMapping的時(shí)候,我們知道會(huì)在spring初始化的時(shí)候,執(zhí)行initApplicationContext()方法,我們閱讀AbstractDetectingUrlHandlerMapping類(lèi)的代碼時(shí),發(fā)現(xiàn)它又重寫(xiě)了initApplicationContext()方法,所以在spring初始化的時(shí)候,會(huì)執(zhí)行該方法。代碼如下:

@Override
public void initApplicationContext() throws ApplicationContextException {
	super.initApplicationContext();
	detectHandlers();
}

其中,super.initApplicationContext()方法調(diào)用了父類(lèi)中的方法,前面已經(jīng)介紹過(guò)了。同時(shí),又調(diào)用了detectHandlers()方法,該方法主要實(shí)現(xiàn)了檢測(cè)容器中注冊(cè)的所有的handler。根據(jù)detectHandlersInAncestorContexts參數(shù)的配置,可以檢查當(dāng)前容器或者當(dāng)前容器及其祖先容器中注冊(cè)的handler。代碼如下:

protected void detectHandlers() throws BeansException {
	ApplicationContext applicationContext = obtainApplicationContext();
	String[] beanNames = (this.detectHandlersInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
			applicationContext.getBeanNamesForType(Object.class));
	// Take any bean name that we can determine URLs for.
	for (String beanName : beanNames) {
		String[] urls = determineUrlsForHandler(beanName);
		if (!ObjectUtils.isEmpty(urls)) {
			// URL paths found: Let's consider it a handler.
			registerHandler(urls, beanName);
		}
	}
	if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
		logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
	}
}

在detectHandlers()方法中,通過(guò)處理器實(shí)例,可以獲取對(duì)應(yīng)的url,該邏輯由determineUrlsForHandler()方法實(shí)現(xiàn),該方法由子類(lèi)BeanNameUrlHandlerMapping中實(shí)現(xiàn)。然后再把urls和對(duì)應(yīng)的handler通過(guò)父類(lèi)AbstractUrlHandlerMapping中的registerHandler()方法,注冊(cè)到Map對(duì)象中。

最后,determineUrlsForHandler()方法的實(shí)現(xiàn),這里主要通過(guò)判斷對(duì)應(yīng)實(shí)例的name或aliases 來(lái)判斷,只有以“/”開(kāi)頭的才是有效的url,代碼如下:

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
	@Override
	protected String[] determineUrlsForHandler(String beanName) {
		List<String> urls = new ArrayList<>();
		if (beanName.startsWith("/")) {
			urls.add(beanName);
		}
		String[] aliases = obtainApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
			if (alias.startsWith("/")) {
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}
}

通過(guò)上面的分析,我們知道:在initApplicationContext()方法中實(shí)現(xiàn)初始化工作,其中AbstractDetectingUrlHandlerMapping類(lèi)主要實(shí)現(xiàn)了處理器的檢測(cè),BeanNameUrlHandlerMapping類(lèi)實(shí)現(xiàn)handler對(duì)應(yīng)url的判斷。

5、其他

通過(guò)前面的分析,我們知道HandlerMapping有兩個(gè)分支的實(shí)現(xiàn),在這篇中我們已經(jīng)分析了AbstractUrlHandlerMapping系列的實(shí)現(xiàn)

由于篇幅原因,AbstractHandlerMethodMapping系列的分析,我們?cè)凇?a href="http://chabaoo.cn/program/296878ugr.htm" target="_blank">詳解SpringMVC組件之HandlerMapping(二)》中進(jìn)行。

到此這篇關(guān)于詳解SpringMVC組件之HandlerMapping(一)的文章就介紹到這了,更多相關(guān)SpringMVC的HandlerMapping內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 實(shí)戰(zhàn)項(xiàng)目錘煉之在線購(gòu)書(shū)商城系統(tǒng)的實(shí)現(xiàn)流程

    Java 實(shí)戰(zhàn)項(xiàng)目錘煉之在線購(gòu)書(shū)商城系統(tǒng)的實(shí)現(xiàn)流程

    讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+jsp+mysql+servlet+ajax實(shí)現(xiàn)一個(gè)在線購(gòu)書(shū)商城系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平
    2021-11-11
  • Springboot jar文件如何打包zip在linux環(huán)境運(yùn)行

    Springboot jar文件如何打包zip在linux環(huán)境運(yùn)行

    這篇文章主要介紹了Springboot jar文件如何打包zip在linux環(huán)境運(yùn)行,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • MyBatis Generator配置入門(mén)

    MyBatis Generator配置入門(mén)

    本文主要介紹了MyBatis Generator配置入門(mén),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Java ServletContext對(duì)象用法解析

    Java ServletContext對(duì)象用法解析

    這篇文章主要介紹了Java ServletContext對(duì)象用法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • Java刪除二叉搜索樹(shù)的任意元素的方法詳解

    Java刪除二叉搜索樹(shù)的任意元素的方法詳解

    這篇文章主要介紹了Java刪除二叉搜索樹(shù)的任意元素的方法,結(jié)合實(shí)例形式詳細(xì)分析了java這對(duì)二叉搜索樹(shù)的遍歷、查找、刪除等相關(guān)操作技巧與使用注意事項(xiàng),需要的朋友可以參考下
    2020-03-03
  • Java連接數(shù)據(jù)庫(kù)JDBC技術(shù)之prepareStatement的詳細(xì)介紹

    Java連接數(shù)據(jù)庫(kù)JDBC技術(shù)之prepareStatement的詳細(xì)介紹

    這篇文章主要介紹了Java連接數(shù)據(jù)庫(kù)JDBC技術(shù)之prepareStatement的詳細(xì)介紹,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • springboot實(shí)現(xiàn)修改請(qǐng)求狀態(tài)404改為200

    springboot實(shí)現(xiàn)修改請(qǐng)求狀態(tài)404改為200

    這篇文章主要介紹了springboot實(shí)現(xiàn)修改請(qǐng)求狀態(tài)404改為200方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Spring的@Scheduled 如何動(dòng)態(tài)更新cron表達(dá)式

    Spring的@Scheduled 如何動(dòng)態(tài)更新cron表達(dá)式

    這篇文章主要介紹了Spring的@Scheduled 如何動(dòng)態(tài)更新cron表達(dá)式的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • SpringBoot連接Redis2種模式解析

    SpringBoot連接Redis2種模式解析

    這篇文章主要介紹了SpringBoot連接Redis2種模式解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • SpringBoot @ModelAttribute使用場(chǎng)景分析

    SpringBoot @ModelAttribute使用場(chǎng)景分析

    這篇文章主要介紹了SpringBoot @ModelAttribute使用場(chǎng)景分析,文中通過(guò)實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08

最新評(píng)論