SpringMVC组件之HandlerMapping分析

目录

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对象。

可以看出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的类图


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

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
3. AbstractHandlerMapping分析


可以看到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.

HandlerMethod的主要继承结构是HandlerMethod–>InvocableHandlerMethod–>ServletInvocableHandlerMethod。

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属性中,以供之后查找用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值