欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringMVC组件之HandlerMapping分析

程序员文章站 2024-02-08 14:29:04
...

目录

1. HandlerMapping概述
2. HandlerMapping的类图
3. AbstractHandlerMapping分析
4. AbstractUrlHandlerMapping族分析
5. AbstracthandlerMethodMapping族分析
6. 总结

1. HandlerMapping概述

在DispatcherServlet的dodispatch方法中有这段断码:

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

在判断完request是不是文件上传请求后,dodispatch调用了getHandler来获取了一个类型为HandlerExecutionChain的对象。而getHandler方法如下:

	/**
	 * Return the HandlerExecutionChain for this request.
	 * <p>Tries all handler mappings in order.
	 * @param request current HTTP request
	 * @return the HandlerExecutionChain, or {@code null} if no handler could be found
	 */
	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping hm : this.handlerMappings) {
				if (logger.isTraceEnabled()) {
					logger.trace(
							"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
				}
				HandlerExecutionChain handler = hm.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

可以看到其遍历了本地的handlerMappings属性,对其中的每一个HandlerMapping调用了getHandler方法,其返回值是一个HandlerExecutionChain对象。
SpringMVC组件之HandlerMapping分析
可以看出HandlerExecutionChain其实就是对Handler和HandlerInterceptor的一个包装。
由此我们可以知道HadlerMapping的作用就是根据传入的HttpServletRequest对象找到相应的HandlerExecutionChain,而HandlerExecutionChain就是对Handler和HandlerIntercptor的包装
而关于DispatcherServlet的初始化在之前也有过介绍了,在其onRefresh方法中调用initStrategies,用这个来初始化其九大组件,对于其中的HandlerMapping组件,初始化逻辑是

  1. 根据detectAllHandlerMappings标志为来决定是否在SpringMVC及其父容器中找到所有的类型为HandlerMapping的bean,如果为True的话,则是从容器中获取,并根据@Order进行排序,否则的话就仅从容器中获取beanname为handlerMapping的bean,detectAllHandlerMappings默认为True。
  2. 如果detectAllHandlerMappings为True而容器中没有类型为HandlerMapping的bean,或者detectAllHandlerMappings为False但容器中没有beanname为handlerMapping的bean的话,就根据DispatcherServlet.properties这个文件中配置的HandlerMapping类名来初始化HandlerMapping.
    很据上面这个逻辑的话,那我们一般配置SpringMVC的时候没有主动的配置HandlerMapping,那应该就是采用的DispatcherServlet.properties这个文件里配置的类作为HandlerMapping了哦。但其实我们一般配置的时候会有一个或者使用@EnableWebMVC,这个标签或者注解其实帮助我们做了一些事情的,比如采用XML配置的话会有:
public class MvcNamespaceHandler extends NamespaceHandlerSupport {

	@Override
	public void init() {
		registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
		registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
		registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
		registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
		registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
		registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
		registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
		registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
		registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
		registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
		registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
		registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
		registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
	}
}

可以看到当配置了annotation-driven后,MvcNamespaceHandler会使用AnnotationDrivenBeanDefinitionParser对XML文件进行解析,而这个Parser有这样的代码:

	public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();

	public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();

也就是说当配置了mvc:annotation-driven时,框架会自动的将HandlerMapping设置为RequestMappingHandlerMapping,将HandlerAdapter设置为RequestMappingHandlerAdapter

2. HandlerMapping的类图

SpringMVC组件之HandlerMapping分析
从这个体系结构我们可以看出,HandlerMapping主要可以分为两类,一类继承自AbstractUrlHandlerMapping,一类继承自AbstractHandlerMethodMapping,这个两个类又都继承了AbstractHandlerMapping,实现了MatchableHandlerMapping。
HandlerMapping是一个函数接口,只有一个方法

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

3. AbstractHandlerMapping分析

SpringMVC组件之HandlerMapping分析
可以看到AnstractHandlerMapping继承了WebApplicationObjectSupport,所以其初始化的时候会调用模版方法initApplicationContext.

	...
	private final List<Object> interceptors = new ArrayList<>();
	private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
	...
	protected void initApplicationContext() throws BeansException {
		extendInterceptors(this.interceptors);
		detectMappedInterceptors(this.adaptedInterceptors);
		initInterceptors();
	}

其中extendInterceptors是个模版方法,默认实现为空。子类可以覆盖这个方法,用以在已有拦截器之前和之后注册额外的拦截器。
detectMappedInterceptors将容器中所有类型为MapperIntercepter类型的bean添加到了adaptedInterceptors属性中

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

initIntercptors的函数内容为:

	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) {
			//使用适配器模式将WebRequestInterceptor适配为HandlerInterceptor
			return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
		}
		else {
			throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
		}
	}

对于interceptors这个属性来说,有两种设置方法,一种是通过HandlerMapping的setInterceptors方法设置,一种是通过子类实现extendInterceptors钩子方法进行设置。
方法detectMappedInterceptor将容器中类型为MapperInterceptor类型的bean查找出来,在这里将查找到的MapperInterceptor存储到了adaptedInterceptors这个属性中了,MapperInterceptor是HandlerInterceptor的子类,用了装饰者模式,除了preHandle。postHandle和afterCompletion之外还提供了match方法,也就是说MapperInterceptor的是由作用范围的,会根据它的includePatterns,excludePatterns来决定是否拦截某个请求路径。
方法initInterceptors,会遍历interceptors属性,先判断其是否为null,是的话就报错,否者
调用adaptInterceptor方法,将其添加到adaptInterceptor属性中。
从以上代码中我们可以得出结论,AbstractHandlerMapping会引用到容器中所有的interceptor,并且将其存储在其adaptedInterceptors属性中。接下来看看其主要方法,getHandler:

	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);
		}

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

	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) {
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

getHandler首先调用了其模版方法getHandlerInternal,交由子类去找handler,然后将子类返回的handler做个判断,如果为空则使用默认的handler,如果默认的handler也为空的话就直接返回null。
得到不为空的handler就判断其是否是string类型,是的话就从容器中获取这个name代表的Bean。
AbstractHandlerMapping有了handler,还有所有的interceptor,现在需要做的就是将handler和需要配置的interceptor一起封装起来形成HandlerExecutionChain,所以其调用了getHandlerExecutionChain,根据request找到了需要的interceptor,这里的interceptor分分为了两类,一类是MappedInterceptor,需要选择性的配置,一类是非MappedInterceptor,这种interceptor会拦截所有的request。
**可以看出,AbstractHandlerMapping拥有容器中所有的interceptor,存储在其adaptedInterceptors属性中。然后交由子类通过getHandlerInternal方法找到request对应的Handler,最后遍历adaptedInterceptors属性找到需要配置的interceptor,最后封装成HandlerExecutionChain对象返回给DispatcherServlet。**在源码中还可以看出AbstractHandlerMapping还和跨域请求有关,这部分就在之后进行分析了。

4. AbstractUrlHandlerMapping族分析

从上面的分析,我们知道了AbstractHandlerMapping的子类需要做的就是实现getHandlerInternal,即通过某种方式去找到request相对应的handler。AbstractUrlHandlerMapping从名字上就可以看出它是通过request的url进行匹配的。这个类簇的大致原理是将url和handler的对应关系用map保存起来,在getHandlerInternal方法中使用url从Map中获取其对应的Handler。AbstractUrlHandlerMapping实现了用url在Map中查找Handler的过程,而Map的初试化则交给了具体的子类去完成。

	protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		Object handler = lookupHandler(lookupPath, request);
		if (handler == null) {
			// 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)) {
				rawHandler = getRootHandler();
			}
			if (rawHandler == null) {
				rawHandler = getDefaultHandler();
			}
			if (rawHandler != null) {
				// Bean name or resolved handler?
				if (rawHandler instanceof String) {
					String handlerName = (String) rawHandler;
					rawHandler = obtainApplicationContext().getBean(handlerName);
				}
				validateHandler(rawHandler, request);
				handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
			}
		}
		if (handler != null && logger.isDebugEnabled()) {
			logger.debug("Mapping [" + lookupPath + "] to " + handler);
		}
		else if (handler == null && logger.isTraceEnabled()) {
			logger.trace("No handler mapping found for [" + lookupPath + "]");
		}
		return handler;
	}

即通过lookupHandler方法去找到handler,
如果没有找到,即handler为null:
    则判断路径是否为“/”,是的话将rootHandler设置给rawHandler(rootHandler是AbstractUrlHandlerMapping的一个属性,用于处理路径为“/”的请求)
如果路径不是"/",或者rootHandler没有设置,则将rawHandler设置为defaultHandler。如果rawHandler不为空,则看rawHandler是否为String类型,如果是String类型,则从容器中获取对应的Bean,然后调用了buildPathExposingHandler给rawHandler添加两个内部拦截器。
从这个过程可以看出,主要的方法就两个lookupHandler与buildPathExposingHandler。

	protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
		// Direct match?
		//先看哈是否能够直接匹配上
		Object handler = this.handlerMap.get(urlPath);
		if (handler != null) {//可以直接通过url找到handler
			// 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);
		}

		// Pattern match?
		//临时变量,因为一个url可能与多个pattern相匹配
		List<String> matchingPatterns = new ArrayList<>();
		for (String registeredPattern : this.handlerMap.keySet()) {
			if (getPathMatcher().match(registeredPattern, urlPath)) {
				matchingPatterns.add(registeredPattern);
			}
			//useTrailingSlashMatch标志代表/AA/等于/AA
			else if (useTrailingSlashMatch()) {
				if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
					matchingPatterns.add(registeredPattern +"/");
				}
			}
		}//到这里已经找到了与request的url相匹配的所有pattren。

		String bestMatch = null;
		Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
		if (!matchingPatterns.isEmpty()) {
			matchingPatterns.sort(patternComparator);
			if (logger.isDebugEnabled()) {
				logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
			}
			//找到与url最优匹配的pattern
			bestMatch = matchingPatterns.get(0);
		}
		if (bestMatch != null) {
			//找到bestMatch相对应的handler
			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 name or resolved handler?
			if (handler instanceof String) {
				String handlerName = (String) handler;
				handler = obtainApplicationContext().getBean(handlerName);
			}
			validateHandler(handler, request);
			String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

			// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
			// for all of them
			Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
			//有可能多个Pattren的顺序相同,再次比较下
			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.isDebugEnabled()) {
				logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
			}
			return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
		}

		// No handler found...
		return null;
	}

虽然说有一个用于request与handler对应的Map,但是大部分情况下也不能直接通过url找到handler。因为有的handler使用了pattern的匹配模式。

buildPathExposingHandler方法用于给查找到的Handler注册两个拦截器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;
	}

	private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {

		private final String bestMatchingPattern;

		private final String pathWithinMapping;

		public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) {
			this.bestMatchingPattern = bestMatchingPattern;
			this.pathWithinMapping = pathWithinMapping;
		}

		@Override
		public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
			exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
			request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
			return true;
		}
	}

	protected void exposePathWithinMapping(String bestMatchingPattern, String pathWithinMapping,
			HttpServletRequest request) {

		request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatchingPattern);
		request.setAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
	}

	private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter {

		private final Map<String, String> uriTemplateVariables;

		public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) {
			this.uriTemplateVariables = uriTemplateVariables;
		}

		@Override
		public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
			exposeUriTemplateVariables(this.uriTemplateVariables, request);
			return true;
		}
	}

	protected void exposeUriTemplateVariables(Map<String, String> uriTemplateVariables, HttpServletRequest request) {
		request.setAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
	}

在这个地方我们可以知道AbstractUrlHandlerMapping的getHandlerInternal会调用lookupHandler,而lookupHandler会从handlerMap中找到与url相匹配的handler,而这个找的过程又可以分为直接查找与通过pattern模式匹配查找,如果能找到的话会调用buildPathExposingHandler方法将一些属性设置到request域中
在这个地方其实也就是一个查找的过程,那我们再去看看查找所需要的handlerMap是怎样建立的。

	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);
		}
	}

	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.
		//如果handler是String类型的且没有设置lazyInitHandlers,则从SpringMVC容器中获取handler.
		if (!this.lazyInitHandlers && handler instanceof String) {
			String handlerName = (String) handler;
			ApplicationContext applicationContext = obtainApplicationContext();
			if (applicationContext.isSingleton(handlerName)) {
				resolvedHandler = applicationContext.getBean(handlerName);
			}
		}

		//从handlerMap中看这个url是否已经注册过handler了,可以多个url对应一个handler,但不能说一个完全一模一样的url对
		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 {
			//如果路径等于/则设置为rootHandler
			if (urlPath.equals("/")) {
				if (logger.isInfoEnabled()) {
					logger.info("Root mapping to " + getHandlerDescription(handler));
				}
				setRootHandler(resolvedHandler);
			}
			//如果路径等于"/*"则设置defaultHandler
			else if (urlPath.equals("/*")) {
				if (logger.isInfoEnabled()) {
					logger.info("Default mapping to " + getHandlerDescription(handler));
				}
				setDefaultHandler(resolvedHandler);
			}
			else {
				this.handlerMap.put(urlPath, resolvedHandler);
				if (logger.isInfoEnabled()) {
					logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
				}
			}
		}
	}

可以看到有两个registerHandler,第一个的参数为String[]和String可以通过这个方法来将多个pattern或者url注册到一个handler上(这里的handler是一个容器中的bean),它会调用第二个方法。第二个则是会先从容器中检查这个url是否已经组册过了,不允许一个url(注意是调用的get方法)对应多个handler。然后还会根据路径是‘/’或者‘/*’来设置rootHandler和defaultHandler。
现在再来看看AbstractUrlHandlerMapping的子类,SimpleUrlHandlerMapping,

	@Override
	public void initApplicationContext() throws BeansException {
		super.initApplicationContext();
		registerHandlers(this.urlMap);
	}

SimpleUrlHandlerMapping重写了父类的initApplicationContext,除了直接调用父类的初试方法之外,还调用了自己的registerHandlers方法:

	protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
		if (urlMap.isEmpty()) {
			logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
		}
		else {
			urlMap.forEach((url, handler) -> {
				// Prepend with slash if not already present.
				if (!url.startsWith("/")) {
					url = "/" + url;
				}
				// Remove whitespace from handler bean name.
				if (handler instanceof String) {
					handler = ((String) handler).trim();
				}
				registerHandler(url, handler);
			});
		}
	}

可以看出SimpleUrlHandlerMapping类就是直接将自身的urlMap内容注册到handlerMap中。
另一个继承自AbstractUrlHandlerMapping的就是AbstractDetecingUrlHandlerMapping,这个类也是非常简单的

public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {

	private boolean detectHandlersInAncestorContexts = false;


	/**
	 * Set whether to detect handler beans in ancestor ApplicationContexts.
	 * <p>Default is "false": Only handler beans in the current ApplicationContext
	 * will be detected, i.e. only in the context that this HandlerMapping itself
	 * is defined in (typically the current DispatcherServlet's context).
	 * <p>Switch this flag on to detect handler beans in ancestor contexts
	 * (typically the Spring root WebApplicationContext) as well.
	 */
	public void setDetectHandlersInAncestorContexts(boolean detectHandlersInAncestorContexts) {
		this.detectHandlersInAncestorContexts = detectHandlersInAncestorContexts;
	}


	/**
	 * Calls the {@link #detectHandlers()} method in addition to the
	 * superclass's initialization.
	 */
	//AbstractDetecingHandlerMapping也是通过重写父类的initApplicationContext来设置handlerMap的
	@Override
	public void initApplicationContext() throws ApplicationContextException {
		super.initApplicationContext();
		detectHandlers();
	}

	/**
	 * Register all handlers found in the current ApplicationContext.
	 * <p>The actual URL determination for a handler is up to the concrete
	 * {@link #determineUrlsForHandler(String)} implementation. A bean for
	 * which no such URLs could be determined is simply not considered a handler.
	 * @throws org.springframework.beans.BeansException if the handler couldn't be registered
	 * @see #determineUrlsForHandler(String)
	 */
	protected void detectHandlers() throws BeansException {
		ApplicationContext applicationContext = obtainApplicationContext();
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for URL mappings in application context: " + applicationContext);
		}
		//在容器中找到所有的bean的name
		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) {
			//这是个模版方法,通过bean的name得出url路径
			String[] urls = determineUrlsForHandler(beanName);
			if (!ObjectUtils.isEmpty(urls)) {
				// URL paths found: Let's consider it a handler.
				//得到对应关系的话就组册到handlerMap中
				registerHandler(urls, beanName);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
				}
			}
		}
	}


	/**
	 * Determine the URLs for the given handler bean.
	 * @param beanName the name of the candidate bean
	 * @return the URLs determined for the bean, or an empty array if none
	 */
	protected abstract String[] determineUrlsForHandler(String beanName);

}

而AbstractDetectingUrlHandlerMapping有子类,BeanNameUrlHandlerMapping,而这个HandlerMapping的源码

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

	/**
	 * Checks name and aliases of the given bean for URLs, starting with "/".
	 */
	@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);
	}
}

它所做的也很简单,就是看看beanName和alias是不是以"/"开头,如果是的话就将其作为url。
至此AbstractUrlHandlerMapping的分析就完毕了,可以看出它的层次结构也是很清晰的,在AbstractUrlHandlerMapping中实现了在handlerMap中通过url查找Handler的逻辑,而handlerMap的初始化则是交给子类来处理的,这里初始化又分为两种,一种是直接给出对应关系,另一种则是在容器中查找beanName或者alias以”/“开头的。

5. AbstracthandlerMethodMapping族分析

AbstractHandlerMethodMapping系列是将Method作为Handler来使用的,这也是平时使用最多的一种Handler–HandlerMethod,例如@RequestMapping所注释的方法就是这种handler.
SpringMVC组件之HandlerMapping分析
HandlerMethod的主要继承结构是HandlerMethod–>InvocableHandlerMethod–>ServletInvocableHandlerMethod。
SpringMVC组件之HandlerMapping分析
HandlerMethod的主要属性为:
Object bean; //用于封转bean
Method method; //用于具体处理请求的method
BeanFactory beanfactory; //主要用于新建HandlerMethod时传入的Handler时String类型的情况,这时要从容器中根据beanName获取到真实的Bean,并将其设置为Handler
Method bridgedMethod; //如果method是bridge method则设置为其所对应的原有方法,否则直接设置为method.这个与泛型有关
Methodparameter[] parameters;//代表处理请求的方法。
关于这个类族,在之后的HandlerAdapter中我们会再次的详细的去看看的。现在就先回到AbstractHandlerMethodMapping中,
AbstractHandlerMethodMapping还实现了InitializingBean,这是bean生命周期中的一个方法,会在参数设置完成后调用其afterPropertiesSet方法,这个方法的内容为:

	@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}

	protected void initHandlerMethods() {
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for request mappings in application context: " + getApplicationContext());
		}
		//从容器中获取所有的beanNames
		String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
				obtainApplicationContext().getBeanNamesForType(Object.class));

		for (String beanName : beanNames) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				Class<?> beanType = null;
				try {
					beanType = obtainApplicationContext().getType(beanName);
				}
				catch (Throwable ex) {
					// An unresolvable bean type, probably from a lazy bean - let's ignore it.
					if (logger.isDebugEnabled()) {
						logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
					}
				}
				//通过isHandler来筛选Handler 这是个模版方法,在RequestMappingHandlerMapping里面实现了这个方法,逻辑就是判断类是否有@Controller或者@RequestMapping注释
				if (beanType != null && isHandler(beanType)) {
					//detectHandlerMethods负责将Handler保存到Map中,
					detectHandlerMethods(beanName);
				}
			}
		}
		//可以对Handler进行一些初试化工作
		handlerMethodsInitialized(getHandlerMethods());
	}

	protected void detectHandlerMethods(final Object handler) {
		//获取实际Handler的类型
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			final Class<?> userType = ClassUtils.getUserClass(handlerType);
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							//这是个模版方法,在RequestMappingHandlerMapping中实现,将@RequestMapping注解的信息转换为RequestMappingInfo
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isDebugEnabled()) {
				logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

到现在我们可以清楚AbstractHandlerMethodMapping的初试化过程,总结起来就是查找容器中所有的beanName,然后遍历这些beanName,对每一个beanName取得其类类型,看它是否是有@Controller或者@RequestMapping注解(这是在RequestMappingHandlerMapping中完成的),如果有的话会调用detectHandlerMethods,这个方法会遍历传入Bean的所有符合条件的方法,即可以理解为有@RequestMapping注解的方法。将@RequestMapping中的信息转换为RequestMappingInfo对象,就形成了method与RequestMappingInfo对应关系的map,最后再将这个Map存储在mappingRegistry属性中.这是AbstractHandlerMethodMapping的内部类MappingRegistry。这个内部类主要有以下属性

		private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

		private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

		private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

		private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

		private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

		private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

看了其初始化过程,再看下其是如何运作的。从之前的分析中知道AbstractHandlerMapping会调用子类的getHandlerInternal方法,因此,我们可以先从这个方法看起走:

	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		this.mappingRegistry.acquireReadLock();
		try {
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			if (logger.isDebugEnabled()) {
				if (handlerMethod != null) {
					logger.debug("Returning handler method [" + handlerMethod + "]");
				}
				else {
					logger.debug("Did not find handler method for [" + lookupPath + "]");
				}
			}
			//createWithResolvedBean是用于处理handlerMethod是String类型的情况
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

现在看这个方法就能看的很清楚了,它就是通过request的lookUpPath找到Handler,即HandlerMethod对象。如果找到的话就调用createWithResolvedBean方法后返回。在这个过程中还用了一个读写锁来保护整个方法,这是因为前面说的MappingRegistry中有线程不安全的集合类。

	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		//Match是个内部类,封装了匹配条件和HandlerMethod两个属性
		List<Match> matches = new ArrayList<>();
		//根据lookupPath直接在mappingRegistry的urlLookup属性中查找T,这个T是类类型,默认是RequestMappingInfo
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
			//最终调用了RequestMappingInfo的getMatchingCondition方法
			addMatchingMappings(directPathMatches, matches, request);
		}
		//如果不能根据lookupPath找到RequestMappingInfo对象,则将所有的RequestMappingInfo都遍历一遍,看是否和request相匹配
		if (matches.isEmpty()) {
			// No choice but to go through all mappings...
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}
		//如果有与request相匹配的RequestMappingInfo
		if (!matches.isEmpty()) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			if (logger.isTraceEnabled()) {
				logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
			}
			//找到最优匹配的那个
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				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();
					throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
							request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
				}
			}
			handleMatch(bestMatch.mapping, lookupPath, request);
			//返回找到的handlerMethod
			return bestMatch.handlerMethod;
		}
		else {
			//返回null
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

6. 总结

在这篇笔记中对HandlerMapping类族做了个简单的分析,可以清楚的是:HandlerMapping就只定义一个方法,getHandler,作用是根据request找到相应的HandlerExecutionChain。HandlerExecutionChain就是对Interceptor和handler的包装。而Handler的定义是丰富多样的,可能是某个类也可能是某个方法,所以后面流程需要交给HandlerMapping处理。而HandlerMapping有个抽象类AbstractHandlerMapping,这个类拥有容器中所有的interceptor,所以关于interceptor的处理就交给了AbstractHandlerMapping,而找Handler的方法就交给了子类去完成,所以子类需要实现模版方法getHandlerInternal。而从request找handler又分为了两大类,一是根据url路径来查找,即AbstractUrlHandlerMapping类族,它又分为SimpleUrlHandlerMapping,它的handlerMap是我们直接告诉容器的,而AbstractDetctingHandlerMapping则是从容器中获取url与bean的对应关系,它的子类BeanNameHandlerMapping就是看beanName或者alias是否以’/'开始。二是根据RequestMappingInfo来查找Handler(即AbstractHandlerMethodMapping),这里的Hanndler就是HandlerMethod的类型。它的基本思路就是查找到容器中所有的以@Controller或者@RequestMapping注解的bean,然后遍历bean中的方法,看方法是否被@RequestMapping注释,有的话就将@RequestMapping中的信息转化为RequestMappingInfo,这是RequestCondition的子类。将RequestMappingInfo和HandlerMethod的对应关系放到Map中,然后将这些信息放到AbstractHandlerMethodMapping的MappingRegistry属性中,以供之后查找用。