java源码 - SpringMVC(4)之 HandlerMapping

<spring.version>5.2.4.RELEASE</spring.version>
贴一个几种HandlerMapping的使用

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。

	/ * *
	*定义的{@link HandlerMapping}实现的抽象基类
	请求和{@link HandlerMethod}之间的映射。
	*
	对于每个注册的处理程序方法,都维护一个惟一的映射
	*定义映射类型{@code <T>}细节的子类。
	*
	* @param <T>包含条件的{@link HandlerMethod}的映射
	*需要匹配一个传入请求的处理程序方法。
	* /
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

6.1 初始化

先找到MappingRegistry这个内部类:

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

在这里插入图片描述
这几个结构的内容初始化就是AbstractHandlerMethodMapping初始化要做的事,该实现了InitializingBean接口,所以spring容器会自动调用其afterPropertiesSet方法。

	/ * *
	*检测处理程序方法在初始化。
	* @see # initHandlerMethods
	* /
	@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}

这里可能会疑惑,其父类不是也有一个初始化方法吗?大家可以思考一下。
其实很明显,是从容器中拿到这块handlermapping的。就是扫描容器加载完全后的操作。
initHandlerMethods进行HandlerMethod的注册操作,简单来说就是从springMVC的容器中获取所有的beanName,注册url和实现方法HandlerMethod的对应关系。

	/ * *
	*扫描ApplicationContext中的bean,检测和注册处理程序方法。
	* @see # getCandidateBeanNames ()
	* @see # processCandidateBean
	* @see # handlerMethodsInitialized
	* /
	protected void initHandlerMethods() {
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
	/ ** 
	*限定作用域代理后面的目标Bean的Bean名称前缀。
	用于从处理程序方法检测中排除那些
	*目标,以支持相应的代理。
	* 我们不检查autowire-candidate状态,这是
	* *代理目标过滤的问题是如何被处理的自动装配水平,
	* 
	* *自autowire-candidate可能已经转向其他
	* * {@code}的原因,同时还希望bean有资格的处理程序方法。
	* * <p>最初在{@link org.springframework.aop.scope中定义。ScopedProxyUtils} 
	* *但是在这里复制是为了避免对spring-aop模块的严格依赖。*/
	private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";
	/ * *
	*在检测到所有处理程序方法后调用。
	带有处理程序方法和映射的只读映射。
	* /
	protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
		// Total包括通过registerMapping检测到的映射+显式注册
		int total = handlerMethods.size();
		if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
			logger.debug(total + " mappings in " + formatMappingName());
		}

接着来看processCandidateBean:

	/ * *
	确定指定的候选bean和调用的类型
	* {@link #detectHandlerMethods}如果标识为处理程序类型。
	这个实现通过检查避免创建bean
	* {@link org.springframework.beans.factory.BeanFactory #方法}
	*并使用bean名调用{@link #detectHandlerMethods}* /
	protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			beanType = obtainApplicationContext().getType(beanName);
		}
		catch (Throwable ex) {
			//一个无法解析的bean类型,可能来自一个懒惰的bean——我们忽略它。
			if (logger.isTraceEnabled()) {
				logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
			}
		}
		if (beanType != null && isHandler(beanType)) {
			detectHandlerMethods(beanName);
		}
	}

根据beanName进行一系列的注册,最终实现是在registerHandlerMethod

	/ * *
	*在指定的处理程序bean中寻找处理程序方法。
	一个bean名称或一个实际的处理程序实例
	* @see # getMappingForMethod
	* /
	protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

注册beanName和method及RequestMappingInfo之间的关系,RequestMappingInfo会保存url信息。

	/ * *
	*注册一个处理程序方法和它的唯一映射。在启动时调用,用于
	*每个检测到的处理程序方法。
	处理程序或处理程序实例的bean名称
	* @param方法注册的方法
	与处理程序方法相关联的映射条件
	* @抛出IllegalStateException,如果已经注册了其他方法
	*在相同的映射下
	* /
	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		this.mappingRegistry.register(mapping, handler, method);
	}

在这里插入图片描述
这样我们就回到我们初开始看到的那几个类:
register方法如下:加入了并发条件控制,并将相应的信息加入到那几个map中去。

	public void register(T mapping, Object handler, Method method) {
			this.readWriteLock.writeLock().lock();
			try {
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				assertUniqueMethodMapping(handlerMethod, mapping);
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					this.urlLookup.add(url, mapping);
				}

				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}

				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					this.corsLookup.put(handlerMethod, corsConfig);
				}

				this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}

6.2 使用(getHandlerInternal)

从org.springframework.web.servlet.DispatcherServlet#getHandler的入口开始:
在这里插入图片描述
来看看DispatcherServlet支持的HandlerMapping的类
在这里插入图片描述
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
在这里插入图片描述
先来查看改对象下的mappingRegistry:
在这里插入图片描述
基本上对应于上面看到的AbstractHandlerMethodMapping。接着往下走:
org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#getHandlerInternal。调用的其子类的getHandlerInternal

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

随后才是org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal

	/ * *
*查找给定请求的处理程序方法。
* /
	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		//加了读写锁
		this.mappingRegistry.acquireReadLock();
		try {
		//通过请求匹配出handler
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
	/ * *
	*查找当前请求的最佳匹配处理程序方法。
	*如果找到多个匹配项,则选择最佳匹配项。
	* @param lookupPath映射当前servlet映射中的查找路径
	* @param请求当前请求
	* @返回最佳匹配的处理程序方法,如果没有匹配则返回{@code null}
	* @see #handleMatch(对象,字符串,HttpServletRequest)
	* @see #handleNoMatch(Set, String, HttpServletRequest)
	* /
	@Nullable
	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	//存储匹配上的结果
		List<Match> matches = new ArrayList<>();
		//基于url的mapping
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		
		if (directPathMatches != null) {
		//扫描注册表mapping
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			//没有选择,只能通过所有映射…
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}
		//匹配到,获取最佳
		if (!matches.isEmpty()) {
		//将结果排序
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			//获取第一个最优解
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				if (logger.isTraceEnabled()) {
					logger.trace(matches.size() + " matching mappings: " + matches);
				}
				if (CorsUtils.isPreFlightRequest(request)) {
					return PREFLIGHT_AMBIGUOUS_MATCH;
				}
				//获取第二个最优解
				Match secondBestMatch = matches.get(1);
				if (comparator.compare(bestMatch, secondBestMatch) == 0) {
				//如果两个优先级相等,那么说明有问题。这两个方法冲突。
					Method m1 = bestMatch.handlerMethod.getMethod();
					Method m2 = secondBestMatch.handlerMethod.getMethod();
					String uri = request.getRequestURI();
					throw new IllegalStateException(
							"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
				}
			}
			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

看看addMatchingMappings方法:

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

这个mapping是
在这里插入图片描述
再回到:
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain
在这里插入图片描述
看看此时的handler:
在这里插入图片描述
已经找到了该链接下对应的方法,并且封装类型是HandlerMethod。

	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	// 对应上面handler 不是
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

在这里插入图片描述

找到对应的拦截器,返回最后的拦截器链。后面的工作就交给HandlerAdapter完成了。
其实可以来看一下他将HandlerMethod封装成HandlerExecutionChain的过程:

	public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {
		if (handler instanceof HandlerExecutionChain) {
			HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
			this.handler = originalChain.getHandler();
			this.interceptorList = new ArrayList<>();
			CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
			CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
		}
		else {
			this.handler = handler;
			this.interceptors = interceptors;
		}
	}


总结:
HandlerMapping的整体结构在AbstractHandlerMapping中设计,简单来说其功能就是根据request找到Handler和Interceptors,组合成HandlerExecutionChain类型并返回。找Handler的过程通过模板方法getHandlerInternal留给子类实现,查找Interceptors则是AbstractHandlerMapping自己完成的,查找的具体方法是通过几个与Interceptor相关的Map完成的,在初始化过程中对那些Map进行了初始化。

难点:初始化,获取。
初始化基本上有spring的源码知识就可以很容易懂了。而且基本上只用看AbstractHandlerMethodMapping,因为用得比较多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值