当前位置: 移动技术网 > IT编程>开发语言>Java > java源码 - SpringMVC(4)之 HandlerMapping

java源码 - SpringMVC(4)之 HandlerMapping

2020年07月16日  | 移动技术网IT编程  | 我要评论

<spring.version>5.2.4.RELEASE</spring.version>

1. 类关系依赖图与继承关系

在这里插入图片描述
主要看红线的,即如下继承关系:
在这里插入图片描述

2. AbstractHandlerMapping

还是见了很多次的模板模式,AbstractHandlerMapping设计了HandlerMapping实现的整体结构,子类只需要通过模板方法提供一些初始值或者具体的实现。
HandlerMapping的作用是根据request查找Handler和Interceptors。获取Handler的过程通过模板方法getHandlerInternal交给了子类。AbstractHandlerMapping中保存了所用配置的Interceptor,在获取到Handler后会自己根据从request提取的lookupPath将相应的Inte-rceptors装配上去,当然子类也可以通过getHandlerInternal方法设置自己的Interceptor,getHandlerInternal的返回值为Object类型。

2.1 初始化

AbstractHandlerMapping继承了WebApplicationObjectSupport,初始化时会自动调用模板方法initApplicationContext,AbstractHandlerMapping的创建就是在initApplicationContext方法里面实现的。

	@Override
	protected void initApplicationContext() throws BeansException {		//钩子函数留给子类实现,将子类添加的拦截器加入到interceptors
		extendInterceptors(this.interceptors);
		//detectMappedInterceptors方法用于将Spring MVC容器及父容器中的所有MappedInter-ceptor类型的Bean添加到adaptedInterceptors属性
		detectMappedInterceptors(this.adaptedInterceptors);
		//initInterceptors方法的作用是初始化Interceptor,具体内容其实是将interceptors属性里所包含的对象按类型添加到adaptedInterceptors
		initInterceptors();
	}
	/ * *
	*子类可以覆盖的扩展钩子来注册额外的拦截器,
	*给定配置的拦截器(参见{@link #setInterceptors})*
	*将在{@link #initInterceptors()}调整指定的代码之前被调用
	*拦截器到{@link HandlerInterceptor}实例。
	*
	默认实现为空。
	* @param拦截器配置的拦截器列表(从不{@code null}),允许
	*在现有的拦截器之前和之后添加更多的拦截器
* /
	protected void extendInterceptors(List<Object> interceptors) {
	}
	/ * *
	*检测类型为{@link MappedInterceptor}的bean,并将其添加到映射的拦截器列表中。
	*
	这是在任何{@link MappedInterceptor MappedInterceptors}之外被调用的,可能已经提供
	*通过{@link #setInterceptors},默认情况下添加所有类型为{@link MappedInterceptor}的bean
	*从当前上下文及其祖先。子类可以重写和改进这个策略。
	添加{@link MappedInterceptor}实例到的空列表
* /
	protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
		mappedInterceptors.addAll(
				BeanFactoryUtils.beansOfTypeIncludingAncestors(
						obtainApplicationContext(), MappedInterceptor.class, true, false).values());
	}
	/ * *
	*初始化指定的拦截器,检查{@link MappedInterceptor}*适应{@link HandlerInterceptor}{@link WebRequestInterceptor HandlerInterceptor}* {@link WebRequestInterceptor}如果需要的话。
	* @see # setInterceptors
	* @see # adaptInterceptor
	* /
	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));
			}
		}
	}

5.x 没有mappedInterceptors
mappedInterceptors:此类Interceptor在使用时需要与请求的url进行匹配,只有匹配成功后才会添加到getHandler的返回值HandlerExecutionChain里。它有两种获取途径:从interceptors获取或者注册到spring的容器中通过detectMappedInterceptors方法获取。

AbstractHandlerMapping中的Interceptor有三个List类型的属性:interceptors、adaptedInterceptors;

  • Interceptors:用于配置Spring MVC的拦截器,有两种设置方式:①注册Handler-Mapping时通过属性设置;②通过子类的extendInterceptors钩子方法进行设置。Interceptors并不会直接使用,而是通过initInterceptors方法按类型分配到adaptedInterceptors中进行使用,Interceptors只用于配置。

  • adaptedInterceptors:这种类型的Interceptor不需要进行匹配,在getHandler中会全部添加到返回值HandlerExecutionChain里面。它只能从interceptors中获取。

2.2 使用

HandlerMapping是通过getHandler方法来获取处理器Handler和拦截器Interceptor的。

	/ * *
	*查找给定请求的处理程序,返回到默认值
	*处理程序,如果没有找到特定的。
	* @param请求当前HTTP请求
	返回对应的处理程序实例,或默认处理程序
	* @see # getHandlerInternal
	* /
	@Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//又是一个模板方法,获取Handler
		Object handler = getHandlerInternal(request);
		if (handler == null) {
		//如果没有就使用默认的Handler
		//默认的Handler保存在AbstractHandler-Mapping的一个Object类型的属性defaultHandler中
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean名称还是解析处理程序?
		if (handler instanceof String) {
			String handlerName = (String) handler;
		//如果找到的Handler是String类型,则以它为名到Spring MVC的容器里查找相应的Bean。
			handler = obtainApplicationContext().getBean(handlerName);
		}
		
		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());
		}

		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}
	/**
	*查找给定请求的处理程序,如果没有,返回{@code null}
	*找到了特定的一个。该方法由{@link #getHandler}调用;
	*一个{@code null}返回值将导致默认处理程序,如果设置了一个。
	对于CORS飞行前请求,此方法应该返回一个不匹配的
	*飞行前的请求,但基于URL的预期实际请求
	*路径,从“访问-控制-请求-方法”头的HTTP方法,和
	*来自“访问-控制-请求-报头”的报头因此允许
	* CORS配置通过{@link #getCorsConfiguration(Object, HttpServletRequest)}获得,
	注意:此方法也可能返回预先构建的{@link HandlerExecutionChain},
	*将处理程序对象与动态确定的拦截程序相结合。
	*静态指定的拦截器将合并到这样一个已有的链中。
	* @param请求当前HTTP请求
	* @返回相应的处理程序实例,如果没有找到,则返回{@code null}
	* @抛出异常,如果有内部错误
	*/
	@Nullable
	protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

在这里插入图片描述
接下来看:

/**
	*为给定的处理程序构建{@link HandlerExecutionChain},包括
	*适用的拦截器。
	默认实现构建一个标准的{@link HandlerExecutionChain}
	*与给定的处理程序,处理程序映射的常见拦截器,和任何
	匹配当前请求URL。拦截器
	*按注册顺序添加。子类可以覆盖它
	*为了扩展/重新排列拦截器列表。
	注意:传递进来的处理程序对象可以是原始处理程序或a
	*预构建的{@link HandlerExecutionChain}。这个方法应该处理这些
	*显式地使用两种情况,要么构建一个新的{@link HandlerExecutionChain}
	*或扩展现有的链。
	如果只是在自定义子类中添加拦截器,请考虑调用
	* {@code超级。getHandlerExecutionChain(处理器,请求)}和调用
	在返回的链对象上* {@link HandlerExecutionChain#addInterceptor}。
	解析的处理程序实例(never {@code null})
	* @param请求当前HTTP请求
	* @return HandlerExecutionChain (never {@code null})
	* @see # getAdaptedInterceptors ()
	*/
	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
		//提取出路径
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
		
		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
		//找到匹配的interceptor 
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				//等于找到对应该路径的拦截器
				if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
			//否则就是全局拦截器,加入执行链
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

再来看看MappedInterceptor 是个什么东西:

/*
*包含并委托对{@link HandlerInterceptor}的调用
*包括(可选排除)拦截器应该应用的路径模式。
*还提供了匹配逻辑来测试拦截器是否适用于给定的请求路径。
MappedInterceptor可以直接注册到any
* {@link org.springframework.web.servlet.handler.AbstractHandlerMethodMapping}。
*此外,类型为{@code MappedInterceptor}的bean被自动检测
* {@code AbstractHandlerMethodMapping}(包括祖先ApplicationContext的)
*有效地意味着拦截器注册为“全局”的所有处理程序映射。
*/
public final class MappedInterceptor implements HandlerInterceptor {

看注释可见,这是一个特定的拦截器。

3. AbstractUrlHandlerMapping

3.1 getHandlerInternal

从名字就可以看出它是通过url来进行匹配的。此系列大致原理是将url与对应的Handler保存在一个Map中,在getHandlerInternal方法中使用url从Map中获取Handler,AbstractUrlHandlerMapping中实现了具体用url从Map中获取Handler的过程,而Map的初始化则交给了具体的子孙类去完成。

在这里插入图片描述

AbstractUrlHandlerMapping重写了父类的getHandlerInternal():

	/ * *
	*查找给定请求的URL路径的处理程序。
	* @param请求当前HTTP请求
	* @返回处理程序实例,如果没有找到则{@code null}
	* /
	@Override
	@Nullable
	protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		request.setAttribute(LOOKUP_PATH, lookupPath);
		Object handler = lookupHandler(lookupPath, request);
		if (handler == null) {
			//我们需要直接关心默认处理程序,因为我们需要这样做
			//还公开它的PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE。
			Object rawHandler = null;
			if ("/".equals(lookupPath)) {
				rawHandler = getRootHandler();
			}
			if (rawHandler == null) {
				rawHandler = getDefaultHandler();
			}
			if (rawHandler != null) {
				// Bean名称还是解析的处理程序?
				if (rawHandler instanceof String) {
					String handlerName = (String) rawHandler;
					rawHandler = obtainApplicationContext().getBean(handlerName);
				}
				//可以用来校验的Handler和request是否匹配,是模板方法
				validateHandler(rawHandler, request);
				handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
			}
		}
		return handler;
	}

lookupHandler方法用于使用lookupPath从Map中查找Handler,不过很多时候并不能直接从Map中get到,因为很多Handler都是用了Pattern的匹配模式,如“/show/article/”,这里的星号可以代表任意内容而不是真正匹配url中的星号,如果Pattern中包含PathVariable也不能直接从Map中获取到。另外,一个url还可能跟多个Pattern相匹配,这时还需要选择其中最优的,所以查找过程其实并不是直接简单地从Map里获取,单独写一个方法来做也是应该的。

	/**
	*查找给定URL路径的处理程序实例。
	*
	支持直接匹配,例如已注册的"/test"匹配"/test",
	*和各种ant风格的模式匹配,例如注册的“/t*”匹配
	*“/测试”和“/团队”。有关详细信息,请参见AntPathMatcher类。
	*
	寻找最精确的模式,其中最精确的定义为
	*最长路径模式。
	@param urlPath bean映射到的URL
	* @param请求当前HTTP请求(暴露映射到的路径)
	* @返回相关的处理程序实例,如果没有找到,则返回{@code null}
	* @see # exposePathWithinMapping
	* @see org.springframework.util.AntPathMatcher
	*/
	@Nullable
	protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
		// 直接匹配
		Object handler = this.handlerMap.get(urlPath);
		if (handler != null) {
			// Bean名称还是解析处理程序?
			if (handler instanceof String) {
				String handlerName = (String) handler;
				handler = obtainApplicationContext().getBean(handlerName);
			}
			validateHandler(handler, request);
			return buildPathExposingHandler(handler, urlPath, urlPath, null);
		}

		// 模式匹配 比如带*号的
		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;
		Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
		if (!matchingPatterns.isEmpty()) {
			//排序
			matchingPatterns.sort(patternComparator);
			if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
				logger.trace("Matching patterns " + matchingPatterns);
			}
			//获取最佳匹配
			bestMatch = matchingPatterns.get(0);
		}
		if (bestMatch != null) {
			handler = this.handlerMap.get(bestMatch);
			if (handler == null) {
				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名称还是解析处理程序?
			if (handler instanceof String) {
				String handlerName = (String) handler;
				handler = obtainApplicationContext().getBean(handlerName);
			}
			validateHandler(handler, request);
			String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

			//可能有多个“最佳模式”,让我们确保我们有正确的URI模板变量匹配他们
		
			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;
	}

再来看返回方法buildPathExposingHandler:

	/ * *
	*为给定的原始处理程序构建一个处理程序对象,公开实际的
	*处理程序,{@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}以及
	*执行处理程序之前的{@link #URI_TEMPLATE_VARIABLES_ATTRIBUTE}*
	默认实现构建一个{@link HandlerExecutionChain}
	*使用一个特殊的拦截器来公开路径属性和uri模板变量
	* @param rawHandler要公开的raw处理程序
	* @param pathWithinMapping path to expose before执行处理程序
	* @param uriTemplateVariables URI模板变量,如果没有找到变量,可以是{@code null}
	* @return最终的处理程序对象
	* /
	
	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;
	}

在buildPathExposingHandler方法中给Handler注册两个内部拦截器PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor,这两个拦截器分别在preHandle中调用了exposePathWithinMapping和exposeUriTemplateVariables方法将相应内容设置到了request的属性。

3.2 初始化

从spring容器或者子类加载handler。

	/**
	*调用{@link #detectHandlers()}方法
	*超类的初始化。
	 */
	@Override
	public void initApplicationContext() throws ApplicationContextException {
		super.initApplicationContext();
		detectHandlers();
	}
	/**
	*注册在当前ApplicationContext中找到的所有处理程序。
	处理程序的实际URL确定取决于具体情况
	* {@link #determineUrlsForHandler(String)}实现。一个豆
	*没有这样的url可以被确定,就是不被认为是一个处理程序。
	* @throws org.springframework.beans。如果处理程序不能注册,则BeansException异常
	* @see # determineUrlsForHandler(字符串)
	 */
	protected void detectHandlers() throws BeansException {

在这里插入图片描述

	/**
	*为给定的URL路径注册指定的处理程序。
	* @param urlPaths该bean应该映射到的url
	处理程序bean的名称
	* @抛出BeansException,如果处理程序不能被注册
	* @抛出IllegalStateException,如果注册的处理程序有冲突
	*/
	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);
		}
	}

注意:registerHandler方法并不是自己调用,也不是父类调用,而是子类调用。

	/**
	*为给定的URL路径注册指定的处理程序。
	@param urlPath bean应该映射到的URL
	处理程序实例或处理程序bean名称字符串
	* (bean名称将自动解析为相应的处理程序bean)
	* @抛出BeansException,如果处理程序不能被注册
	* @抛出IllegalStateException,如果注册的处理程序有冲突
	*/
	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;

		// Eagerly resolve handler if referencing singleton via name.
		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) {
			if (mappedHandler != resolvedHandler) {
				throw new IllegalStateException(
						"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
						"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
			}
		}
		else {
			if (urlPath.equals("/")) {
				if (logger.isTraceEnabled()) {
					logger.trace("Root mapping to " + getHandlerDescription(handler));
				}
				setRootHandler(resolvedHandler);
			}
			else if (urlPath.equals("/*")) {
				if (logger.isTraceEnabled()) {
					logger.trace("Default mapping to " + getHandlerDescription(handler));
				}
				setDefaultHandler(resolvedHandler);
			}
			else {
				this.handlerMap.put(urlPath, resolvedHandler);
				if (logger.isTraceEnabled()) {
					logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
				}
			}
		}
	}

AbstractUrlHandlerMapping里面定义了整体架构,子类只需要将Map初始化就可以了。

4. SimpleUrlHandlerMapping

SimpleUrlHandlerMapping定义了一个Map变量(自己定义一个Map主要有两个作用,第一是方便配置,第二是可以在注册前做一些预处理,如确保所有url都以“/”开头),将所有的url和Handler的对应关系放在里面,最后注册到父类的Map中;而AbstractDetectingUrlHandlerMapping则是将容器中的所有bean都拿出来,按一定规则注册到父类的Map中。

	/**
	*调用{@link #registerHandlers}方法
	*超类的初始化。
	 */
	@Override
	public void initApplicationContext() throws BeansException {
		super.initApplicationContext();
		registerHandlers(this.urlMap);
	}

registerHandlers内部又调用了Abstract-UrlHandlerMapping的registerHandler方法将我们配置的urlMap注册到AbstractUrlHandler-Mapping的Map中。

	/**
	*注册URL映射中指定的对应路径的所有处理程序。
	@param urlMap以URL路径作为键,处理程序bean或bean名称作为值的映射
	* @抛出BeansException,如果处理程序无法注册
	* @抛出IllegalStateException,如果注册的处理程序有冲突
	*/
	protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
		if (urlMap.isEmpty()) {
			logger.trace("No patterns in " + formatMappingName());
		}
		else {
			urlMap.forEach((url, handler) -> {
				//如果还没有出现,则在前加上斜杠。
				if (!url.startsWith("/")) {
					url = "/" + url;
				}
				//从处理程序bean名称中删除空格。
				if (handler instanceof String) {
					handler = ((String) handler).trim();
				}
				//调用父类的负责方法
				registerHandler(url, handler);
			});
			if (logger.isDebugEnabled()) {
				List<String> patterns = new ArrayList<>();
				if (getRootHandler() != null) {
					patterns.add("/");
				}
				if (getDefaultHandler() != null) {
					patterns.add("/**");
				}
				patterns.addAll(getHandlerMap().keySet());
				logger.debug("Patterns " + patterns + " in " + formatMappingName());
			}
		}
	}

org.springframework.web.servlet.handler.AbstractUrlHandlerMapping:
在这里插入图片描述

5. AbstractDetectingUrlHandlerMapping

AbstractDetectingUrlHandlerMapping也是通过重写initApplicationContext来注册Handler的,里面调用了detectHandlers方法,在detectHandlers中根据配置的detectHand-lersInAn-cestorContexts参数从Spring MVC容器或者Spring MVC及其父容器中找到所有bean的beanName,然后用determineUrlsForHandler方法对每个beanName解析出对应的urls,如果解析结果不为空则将解析出的urls和beanName(作为Handler)注册到父类的Map,注册方法依然是调用AbstractUrlHandlerMapping的registerHandler方法。
使用beanName解析urls的determineUrlsForHandler方法是模板方法,交给具体子类实现。

	/**
	*调用{@link #detectHandlers()}方法
	*超类的初始化。
	*/
	@Override
	public void initApplicationContext() throws ApplicationContextException {
		super.initApplicationContext();
		detectHandlers();
	}
	/**
	 *注册在当前ApplicationContext中找到的所有处理程序。
	处理程序的实际URL确定取决于具体情况
	* {@link #determineUrlsForHandler(String)}实现。一个豆
	*没有这样的url可以被确定,就是不被认为是一个处理程序。
	* @throws org.springframework.beans。如果处理程序不能注册,则BeansException异常
	* @see # determineUrlsForHandler(字符串)
	*/
	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路径:让我们把它看作一个处理程序。
				//调用父类的registerHandler
				registerHandler(urls, beanName);
			}
		}

		if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
			logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
		}
	}
	/ * *
	*确定给定处理程序bean的url。
	* @param beanName候选bean的名称
	@返回为bean确定的url,如果没有,则返回空数组
	* /
	protected abstract String[] determineUrlsForHandler(String beanName);

目前它的子类只有:
在这里插入图片描述
BeanNameUrlHandlerMapping是检查beanName和alias是不是以“/”开头,如果是则将其作为url,里面只有一个determineUrlsForHandler方法:

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
	/ * *
	*检查给定bean的url名称和别名,以“/”开头。
	* /
	@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);
	}
}

至此,关于HandlerMapping其实只要搞清楚,handler从哪里来,handler怎么调用,就差不多可以弄明白了。其他的基本上就是为了满足框架的可扩展性而设计使用的设计模式。

6. AbstractHandlerMethodMapping

AbstractHandlerMethodMapping直接继承自AbstractHandlerMapping。

AbstractHandlerMethodMapping系列是将Method作为Handler来使用的,这也是我们现在用得最多的一种Handler。
比如经常使用的@RequestMapping所注释的方法就是这种Handler,它专门有一个类型——HandlerMethod,也就是Method类型的Handler。

6.1 初始化

先找到MappingRegistry这个内部类:

	/ * *
	*维护所有映射到处理程序方法,暴露方法的注册表
	*执行查找和提供并发访问。
	*
	Package-private用于测试目的。
	* /
	class MappingRegistry {

本文地址:https://blog.csdn.net/littlewhitevg/article/details/107347814

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网