SpringMVC源码解读--HandlerMapping代码解读

Posted 低调的洋仔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC源码解读--HandlerMapping代码解读相关的知识,希望对你有一定的参考价值。

 

AbstractHandlerMapping

AbstractHandlerMapping这个类,在父类进行设置容器的时候,就会被调用到的。那么调用的是

 

	/**
	 * Initializes the interceptors.
	 * @see #extendInterceptors(java.util.List)
	 * @see #initInterceptors()
	 */
	@Override
	protected void initApplicationContext() throws BeansException 
		extendInterceptors(this.interceptors);
		detectMappedInterceptors(this.mappedInterceptors);
		initInterceptors();
	

 

	/**
	 * Detect beans of type @link MappedInterceptor and add them to the list of mapped interceptors.
	 * <p>This is called in addition to any @link MappedInterceptors that may have been provided
	 * via @link #setInterceptors, by default adding all beans of type @link MappedInterceptor
	 * from the current context and its ancestors. Subclasses can override and refine this policy.
	 * @param mappedInterceptors an empty list to add @link MappedInterceptor instances to
	 */
	protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) 
		mappedInterceptors.addAll(
				BeanFactoryUtils.beansOfTypeIncludingAncestors(
						getApplicationContext(), MappedInterceptor.class, true, false).values());
	

 

 

initInterceptors主要是放了两种的拦截器进来,一种是mappedInterceptors另一种是adaptedInterceptors,然后mappedInterceptors类型的拦截器的话只能是匹配用户请求的url才能放到用户的请求的拦截链子中去,但是后面这个adaptedInterceptors的话就可以直接放进去的。

	/**
	 * Initialize the specified interceptors, checking for @link MappedInterceptors and
	 * adapting @link HandlerInterceptors and @link WebRequestInterceptors if necessary.
	 * @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");
				
				if (interceptor instanceof MappedInterceptor) 
					this.mappedInterceptors.add((MappedInterceptor) interceptor);
				
				else 
					this.adaptedInterceptors.add(adaptInterceptor(interceptor));
				
			
		
	


真正使用的过程中一般是使用getHandler来获取拦截器和处理器的handler的。

 

/**
	 * Look up a handler for the given request, falling back to the default
	 * handler if no specific one is found.
	 * @param request current HTTP request
	 * @return the corresponding handler instance, or the default handler
	 * @see #getHandlerInternal
	 */
	@Override
	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 = getApplicationContext().getBean(handlerName);
		
		return getHandlerExecutionChain(handler, request);
	

 

	/**
	 * Build a @link HandlerExecutionChain for the given handler, including
	 * applicable interceptors.
	 * <p>The default implementation builds a standard @link HandlerExecutionChain
	 * with the given handler, the handler mapping's common interceptors, and any
	 * @link MappedInterceptors matching to the current request URL. Subclasses
	 * may override this in order to extend/rearrange the list of interceptors.
	 * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a
	 * pre-built @link HandlerExecutionChain. This method should handle those
	 * two cases explicitly, either building a new @link HandlerExecutionChain
	 * or extending the existing chain.
	 * <p>For simply adding an interceptor in a custom subclass, consider calling
	 * @code super.getHandlerExecutionChain(handler, request) and invoking
	 * @link HandlerExecutionChain#addInterceptor on the returned chain object.
	 * @param handler the resolved handler instance (never @code null)
	 * @param request current HTTP request
	 * @return the HandlerExecutionChain (never @code null)
	 * @see #getAdaptedInterceptors()
	 */
	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) 
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
		chain.addInterceptors(getAdaptedInterceptors());
		// 这里将刚才设置的mappedInterceptors中的url进行匹配,就是上面提到的部分了。
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
		for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) 
			if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) 
				chain.addInterceptor(mappedInterceptor.getInterceptor());
			
		

		return chain;
	
		// 这里将刚才设置的mappedInterceptors中的url进行匹配,就是上面提到的部分了。
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
		for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) 
			if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) 
				chain.addInterceptor(mappedInterceptor.getInterceptor());
			
		

		return chain;
	

 

 

 

 

 

 

AbstractURLHandlerMapping

 

public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping 

大致的原理是将url与handler保存在一个Map里面。

 

 

	/**
	 * Look up a handler for the URL path of the given request.
	 * @param request current HTTP request
	 * @return the handler instance, or @code null if none found
	 */
	@Override
	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();// 如果是/的话就默认用了RootHandler了。
			
			if (rawHandler == null) 
				rawHandler = getDefaultHandler();// 如果是空的话就默认的handler。
			
			if (rawHandler != null) 
				// Bean name or resolved handler?
				if (rawHandler instanceof String) 
					String handlerName = (String) rawHandler;
					rawHandler = getApplicationContext().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,它会对其进行排序的,然后排序完之后,挑选第一个,但是,可能并不是只有一个的得分情况是第一个的那个值,也就是说可能存在多个url实际上和第一个的url的得分情况是一样的,那么就应该扫描后续的那些url部分,然后将她们放在了uriTemplateVariables里面。

 

	/**
	 * Look up a handler instance for the given URL path.
	 * <p>Supports direct matches, e.g. a registered "/test" matches "/test",
	 * and various Ant-style pattern matches, e.g. a registered "/t*" matches
	 * both "/test" and "/team". For details, see the AntPathMatcher class.
	 * <p>Looks for the most exact pattern, where most exact is defined as
	 * the longest path pattern.
	 * @param urlPath URL the bean is mapped to
	 * @param request current HTTP request (to expose the path within the mapping to)
	 * @return the associated handler instance, or @code null if not found
	 * @see #exposePathWithinMapping
	 * @see org.springframework.util.AntPathMatcher
	 */
	protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception 
		// Direct match?
		Object handler = this.handlerMap.get(urlPath);
		if (handler != null) 
			// Bean name or resolved handler?
			if (handler instanceof String) 
				String handlerName = (String) handler;
				handler = getApplicationContext().getBean(handlerName);
			
			validateHandler(handler, request);
			return buildPathExposingHandler(handler, urlPath, urlPath, null);
		
		// Pattern match?
		List<String> matchingPatterns = new ArrayList<String>();
		for (String registeredPattern : this.handlerMap.keySet()) 
			if (getPathMatcher().match(registeredPattern, urlPath)) 
				matchingPatterns.add(registeredPattern);
			
		
		String bestPatternMatch = null;
		Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
		if (!matchingPatterns.isEmpty()) 
			Collections.sort(matchingPatterns, patternComparator);
			if (logger.isDebugEnabled()) 
				logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
			
			bestPatternMatch = matchingPatterns.get(0);
		
		if (bestPatternMatch != null) 
			handler = this.handlerMap.get(bestPatternMatch);
			// Bean name or resolved handler?
			if (handler instanceof String) 
				String handlerName = (String) handler;
				handler = getApplicationContext().getBean(handlerName);
			
			validateHandler(handler, request);
			String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, 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<String, String>();
			for (String matchingPattern : matchingPatterns) 
				if (patternComparator.compare(bestPatternMatch, 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, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
		
		// No handler found...
		return null;
	


buildPathExposingHandler

 

 

	/**
	 * Build a handler object for the given raw handler, exposing the actual
	 * handler, the @link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, as well as
	 * the @link #URI_TEMPLATE_VARIABLES_ATTRIBUTE before executing the handler.
	 * <p>The default implementation builds a @link HandlerExecutionChain
	 * with a special interceptor that exposes the path attribute and uri template variables
	 * @param rawHandler the raw handler to expose
	 * @param pathWithinMapping the path to expose before executing the handler
	 * @param uriTemplateVariables the URI template variables, can be @code null if no variables found
	 * @return the final handler object
	 */
	protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
			String pathWithinMapping, 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;
	

 

 

这里的俩拦截器本身就是用于将当前的Url实际匹配的Pattern、匹配条件,和URL模版参数设置到Request请求中去。这样以后就可以从url中获取了。

 

 

 

	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(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
			return true;
		

	
	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;
		
	

 

总结下:

从DespatcherServelet类中的getHandler开始进行调用,然后调用了AbstractHandlerMapping中的getHandler方法,然后里面有个getHandlerInternal方法,这个方法要求子类进行实现的,那么,在其子类中AbstractUrlHandlerMapping中,实现了这个方法,其中比较重要的是lookupHandler方法,然后这个方法会获取handlerMap中的url的映射对象,如果没有的话就说明直接获取不到,只能模式匹配通过url的匹配确定一个集合,然后排序,然后将排序结果选取第一个返回,但是也会有多个价值同等的情况,那么这些同等的元素将会全部封装进一个集合中去,然后这个结合连同刚才匹配到的第一个结果,通过封装进一个执行ExecutionChain中去,然后层层返回,在getHandler接收到返回值之后还要检查是不是string类型的,如果是的话就尝试获取该字符串对应的对象的实例,到这里确保了一个比较重要的概念,是handler到底是谁的问题,之前是各种字符串的handler其实是url的,但是为了映射成能够被调用的handler也就是controller等的类的实例,只能getBean的形式来从容器中获取类的实例了,最后将返回的结果封装称为执行链,但是这里需要注意的是getHandlerInternal返回的handler是经过处理以后的,有可能会已经封装为chain了,所以在最后封装成chain链的时候会进行一个确认,到底需要不需要自己进一步封装了。

 

里面这个handlerMap子类会自动的进行注册的。

AbstractUrlHandlerMapping

 

 

	/**
	 * Register the specified handler for the given URL paths.
	 * @param urlPaths the URLs that the bean should be mapped to
	 * @param beanName the name of the handler bean
	 * @throws BeansException if the handler couldn't be registered
	 * @throws IllegalStateException if there is a conflicting handler registered
	 */
	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);
		
	

	/**
	 * Register the specified handler for the given URL path.
	 * @param urlPath the URL the bean should be mapped to
	 * @param handler the handler instance or handler bean name String
	 * (a bean name will automatically be resolved into the corresponding handler bean)
	 * @throws BeansException if the handler couldn't be registered
	 * @throws IllegalStateException if there is a conflicting handler registered
	 */
	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;
			if (getApplicationContext().isSingleton(handlerName)) 
				resolvedHandler = getApplicationContext().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.isInfoEnabled()) 
					logger.info("Root mapping to " + getHandlerDescription(handler));
				
				setRootHandler(resolvedHandler);// 如果是斜杠就设置为roothandler
			
			else if (urlPath.equals("/*")) 
				if (logger.isInfoEnabled()) 
					logger.info("Default mapping to " + getHandlerDescription(handler));
				
				setDefaultHandler(resolvedHandler);// 如果是/*就设置为defaultHandler
			
			else 
				this.handlerMap.put(urlPath, resolvedHandler);
				if (logger.isInfoEnabled()) 
					logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
				
			
		
	


上面的子类调用,注册使用的。

 

 

SimpleUrlHandlerMapping

 

 

public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping 

 

 

调用了一个父类的initApplicationContext,然后调用registerHandlers方法,来完成注册handler,然后调用的就是父类的registerHandler方法了、

 

	/**
	 * Calls the @link #registerHandlers method in addition to the
	 * superclass's initialization.
	 */
	@Override
	public void initApplicationContext() throws BeansException 
		super.initApplicationContext();
		registerHandlers(this.urlMap);
	

	/**
	 * Register all handlers specified in the URL map for the corresponding paths.
	 * @param urlMap Map with URL paths as keys and handler beans or bean names as values
	 * @throws BeansException if a handler couldn't be registered
	 * @throws IllegalStateException if there is a conflicting handler registered
	 */
	protected void registerHandlers(Map<String, Object> urlMap) throws BeansException 
		if (urlMap.isEmpty()) 
			logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
		
		else 
			for (Map.Entry<String, Object> entry : urlMap.entrySet()) 
				String url = entry.getKey();
				Object handler = entry.getValue();
				// 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);
			
		
	

 

 

 

 

 

AbstractDetectingUrlHandlerMapping

 

/**
	 * Calls the @link #detectHandlers() method in addition to the
	 * superclass's initialization.
	 */
	@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 
		if (logger.isDebugEnabled()) 
			logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
		// 获取容器中的所有bean的名字
		String[] beanNames = (this.detectHandlersInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
				getApplicationContext().getBeanNamesForType(Object.class));
		// 对每个beanName解析url,如果能解析到的话就注册到父类的map中。
		// Take any bean name that we can determine URLs for.
		for (String beanName : beanNames) 
			String[] urls = determineUrlsForHandler(beanName);
			if (!ObjectUtils.isEmpty(urls)) // 如果能解析到就注册到父类
				// URL paths found: Let's consider it a handler.
				registerHandler(urls, beanName);
			
			else 
				if (logger.isDebugEnabled()) 
					logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
				
			
		
			// 对每个beanName解析url,如果能解析到的话就注册到父类的map中。
		// Take any bean name that we can determine URLs for.
		for (String beanName : beanNames) 
			String[] urls = determineUrlsForHandler(beanName);
			if (!ObjectUtils.isEmpty(urls)) // 如果能解析到就注册到父类
				// URL paths found: Let's consider it a handler.
				registerHandler(urls, beanName);
			
			else 
				if (logger.isDebugEnabled()) 
					logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
				
			
		
	

 

 

 

 

 

BeanNameUrlHandlerMapping

 

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<String>();
		if (beanName.startsWith("/")) 
			urls.add(beanName);
		
		String[] aliases = getApplicationContext().getAliases(beanName);
		for (String alias : aliases) 
			if (alias.startsWith("/")) 
				urls.add(alias);
			
		
		return StringUtils.toStringArray(urls);
	

设置beanName和alias是不是以/开头,如果是的话就将其作为url.

 

 

AbstractControllerUrlHandlerMapping

 

	/**
	 * This implementation delegates to @link #buildUrlsForHandler,
	 * provided that @link #isEligibleForMapping returns @code true.
	 */
	@Override
	protected String[] determineUrlsForHandler(String beanName) 
		Class<?> beanClass = getApplicationContext().getType(beanName);
		if (isEligibleForMapping(beanName, beanClass)) 
			return buildUrlsForHandler(beanName, beanClass);
		
		else 
			return null;
		
	

将实现了Controller或者是标记了Controller注解的类的bean作为handler,并且可以通过设置excludedClasses和excludedPackages将不包含的bean或者不包含的包下的所有bean排除在外。

 

 

	protected boolean isEligibleForMapping(String beanName, Class<?> beanClass) 
		if (beanClass == null) 
			if (logger.isDebugEnabled()) 
				logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
						"because its bean type could not be determined");
			
			return false;
		
		if (this.excludedClasses.contains(beanClass)) 
			if (logger.isDebugEnabled()) 
				logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
						"because its bean class is explicitly excluded: " + beanClass.getName());
			
			return false;
		
		String beanClassName = beanClass.getName();
		for (String packageName : this.excludedPackages) 
			if (beanClassName.startsWith(packageName)) 
				if (logger.isDebugEnabled()) 
					logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
							"because its bean class is defined in an excluded package: " + beanClass.getName());
				
				return false;
			
		
		return isControllerType(beanClass);// 是不是controller类型
	


ControllerClassNameHandlerMapping和ControllerBeanNameHandlerMapping,一个是使用了beanName作为url一个是使用的className作为的url.

 

 

AbstractHandlerMethodMapping

因为他实现了一个InitializingBean所以会自动调用其

/**
 * Detects handler methods at initialization.
 */
@Override
public void afterPropertiesSet() 
   initHandlerMethods();

这个方法,然后调用initHandlerMethods方法。

 

 

protected void initHandlerMethods() 
		if (logger.isDebugEnabled()) 
			logger.debug("Looking for request mappings in application context: " + getApplicationContext());
		

		String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
				getApplicationContext().getBeanNamesForType(Object.class));

		for (String beanName : beanNames) 
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
					isHandler(getApplicationContext().getType(beanName)))
				detectHandlerMethods(beanName);
			
		
		handlerMethodsInitialized(getHandlerMethods());
	


detectHandlerMethods方法

 

 

protected void detectHandlerMethods(final Object handler) 
		Class<?> handlerType =
				(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());

		// Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
		final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
		final Class<?> userType = ClassUtils.getUserClass(handlerType);

		Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() 
			@Override
			public boolean matches(Method method) 
				T mapping = getMappingForMethod(method, userType);
				if (mapping != null) 
					mappings.put(method, mapping);
					return true;
				
				else 
					return false;
				
			
		);

		for (Method method : methods) // 将符合要求的handler保存到三个map中去。
			registerHandlerMethod(handler, method, mappings.get(method));
		
	

 

 

 

/**
	 * Register a handler method and its unique mapping.
	 * @param handler the bean name of the handler or the handler instance
	 * @param method the method to register
	 * @param mapping the mapping conditions associated with the handler method
	 * @throws IllegalStateException if another method was already registered
	 * under the same mapping
	 */
	protected void registerHandlerMethod(Object handler, Method method, T mapping) 
		HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
		HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
		if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) //检查是不是已经存在了,如果已经存在了的话就看是不是一样的,如果不一样要抛出异常的。
			throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
					"' bean method \\n" + newHandlerMethod + "\\nto " + mapping + ": There is already '" +
					oldHandlerMethod.getBean() + "' bean method\\n" + oldHandlerMethod + " mapped.");
		
		// 添加handlermethods
		this.handlerMethods.put(mapping, newHandlerMethod);
		if (logger.isInfoEnabled()) 
			logger.info("Mapped \\"" + mapping + "\\" onto " + newHandlerMethod);
		

		Set<String> patterns = getMappingPathPatterns(mapping);
		for (String pattern : patterns) 
			if (!getPathMatcher().isPattern(pattern)) 
				this.urlMap.add(pattern, mapping);// 添加urlMap中
			
		

		if (this.namingStrategy != null) 
			String name = this.namingStrategy.getName(newHandlerMethod, mapping);
			updateNameMap(name, newHandlerMethod);// 添加updateNameMap中、
		
			// 添加handlermethods
		this.handlerMethods.put(mapping, newHandlerMethod);
		if (logger.isInfoEnabled()) 
			logger.info("Mapped \\"" + mapping + "\\" onto " + newHandlerMethod);
		

		Set<String> patterns = getMappingPathPatterns(mapping);
		for (String pattern : patterns) 
			if (!getPathMatcher().isPattern(pattern)) 
				this.urlMap.add(pattern, mapping);// 添加urlMap中
			
		

		if (this.namingStrategy != null) 
			String name = this.namingStrategy.getName(newHandlerMethod, mapping);
			updateNameMap(name, newHandlerMethod);// 添加updateNameMap中、
		
	


在AbstractHandlerMapping中实际上在调用getHandler的时候调用了一个getInternalHandler方法,

 

这个方法在AbstractHandlerMethodMapping中也进行了实现。

 

	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception 
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) 
			logger.debug("Looking up handler method for path " + lookupPath);
		
		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 + "]");
			
		
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	

 

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception 
		List<Match> matches = new ArrayList<Match>();
		List<T> directPathMatches = this.urlMap.get(lookupPath);
		if (directPathMatches != null) 
			addMatchingMappings(directPathMatches, matches, request);
		
		if (matches.isEmpty()) 
			// No choice but to go through all mappings...
			addMatchingMappings(this.handlerMethods.keySet(), matches, request);
		

		if (!matches.isEmpty()) 
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			Collections.sort(matches, comparator);
			if (logger.isTraceEnabled()) 
				logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
			
			Match bestMatch = matches.get(0);// 排序后取出第一个
			if (matches.size() > 1) 
				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);
			return bestMatch.handlerMethod;
		
		else // 无法使用lookupPath得到匹配条件,将所有的匹配条件加入matches中去。
			return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
		
	

 

 

 

RequestMappingHandlerMapping

 

 

 

	/**
	 * Uses method and type-level @@link RequestMapping annotations to create
	 * the RequestMappingInfo.
	 * @return the created RequestMappingInfo, or @code null if the method
	 * does not have a @code @RequestMapping annotation.
	 * @see #getCustomMethodCondition(Method)
	 * @see #getCustomTypeCondition(Class)
	 */
	@Override
	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) 
		RequestMappingInfo info = null;
		RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
		if (methodAnnotation != null) 
			RequestCondition<?> methodCondition = getCustomMethodCondition(method);
			info = createRequestMappingInfo(methodAnnotation, methodCondition);
			RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
			if (typeAnnotation != null) 
				RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
				info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
			
		
		return info;
	

根据@RequestMapping来找方法的,如果没有注解的,就返回null了。

 

 

总结一下:

如何匹配的方法呢?

封装,将方法封装成一个RequestMappingInfo类,里面封装了多个的相关的方法标记中可能会有的参数。以次来标识一个方法。

 

public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> 

	private final String name;

	private final PatternsRequestCondition patternsCondition;

	private final RequestMethodsRequestCondition methodsCondition;

	private final ParamsRequestCondition paramsCondition;

	private final HeadersRequestCondition headersCondition;

	private final ConsumesRequestCondition consumesCondition;

	private final ProducesRequestCondition producesCondition;

	private final RequestConditionHolder customConditionHolder;


 

 

 

 

 

 

 

整个Method类型的HandlerMapping的话主要的流程如下:

首先是AbstractHandlerMethodMapping类实现了InitilizingBean接口,那么会现调用afterPropertiesSet来设置其参数,调用initMethodHandler方法,然后这个方法中会调用detectHandlerMethod方法,这个方法会将handler保存在Map中去,

 

protected void detectHandlerMethods(final Object handler) 
		Class<?> handlerType =
				(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());

		// Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
		final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
		final Class<?> userType = ClassUtils.getUserClass(handlerType);

		Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() 
			@Override
			public boolean matches(Method method) 
				T mapping = getMappingForMethod(method, userType);
				if (mapping != null) 
					mappings.put(method, mapping);// 筛选出来能够进行匹配的方法添加到这个map中去。
					return true;
				
				else 
					return false;
				
			
		);

		for (Method method : methods) 
			registerHandlerMethod(handler, method, mappings.get(method));// 进行相应的注册
		
	

注册分为三个不同的map

 

一个handlerMap保存的是匹配条件和方法的映射关系。

一个urlMap保存的是url和方法的映射关系。

一个nameMap是保存的方法名和MethodHandler的对应关系

后面这俩属于多值的情况。

 

protected void registerHandlerMethod(Object handler, Method method, T mapping) 
		HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
		HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
		if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) 
			throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
					"' bean method \\n" + newHandlerMethod + "\\nto " + mapping + ": There is already '" +
					oldHandlerMethod.getBean() + "' bean method\\n" + oldHandlerMethod + " mapped.");
		

		this.handlerMethods.put(mapping, newHandlerMethod);
		if (logger.isInfoEnabled()) 
			logger.info("Mapped \\"" + mapping + "\\" onto " + newHandlerMethod);
		

		Set<String> patterns = getMappingPathPatterns(mapping);
		for (String pattern : patterns) 
			if (!getPathMatcher().isPattern(pattern)) 
				this.urlMap.add(pattern, mapping);
			
		

		if (this.namingStrategy != null) 
			String name = this.namingStrategy.getName(newHandlerMethod, mapping);
			updateNameMap(name, newHandlerMethod);
		
	

上面完成一个初始化的过程,然后调用过程中会调用其handlerInternal方法

 

 

	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception 
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) 
			logger.debug("Looking up handler method for path " + lookupPath);
		
		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 + "]");
			
		
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	

这个方法里面主要是lookupHandlerMethod方法了。

 

 

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception 
		List<Match> matches = new ArrayList<Match>();
		List<T> directPathMatches = this.urlMap.get(lookupPath);// 根据url获取可以进行匹配的method
		if (directPathMatches != null) // 将匹配的条件添加到matches中去
			addMatchingMappings(directPathMatches, matches, request);
		
		if (matches.isEmpty()) // 没有匹配条件
			// No choice but to go through all mappings...所有的匹配条件全部添加进去
			addMatchingMappings(this.handlerMethods.keySet(), matches, request);
		

		if (!matches.isEmpty()) // 排序查询
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			Collections.sort(matches, comparator);
			if (logger.isTraceEnabled()) 
				logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
			
			Match bestMatch = matches.get(0);// 第一个获取到
			if (matches.size() > 1) // 前两个相同抛出异常
				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);
			return bestMatch.handlerMethod;
		
		else 
			return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
		
	

 

 

 

 

 


再总之:

整个的流程基本上就是先初始化基本数据,首先在AbstractHandlerMapping中会默认初始化一些拦截器,然后也与部分请求的路径等进行匹配,一旦匹配成功可以直接添加上,不过也存在不用匹配直接添加的情况。

AbstractUrlHandlerMapping这个是针对Url进行匹配的,主要的目的是找到Controller的类,它是根据AbstractHandlerMapping中的方法,getHandler方法也是这种类的外界访问切入口来调用的,内部访问了getHandlerInternal方法,然后这个方法会调用lookuppath寻找能够进行匹配的url对应的controller,然后在匹配的过程中如果直接能匹配就返回,不能的话就采用模式匹配,然后匹配结果再排个序,然后获取第零个作为返回的。不过如果存在多个和第零个价值一样的也会将其封装进一个chain中返回,这个chain是作为多重情况返回的,返回的结果回到父类AbstractHandlerMapping的getHandler中去,然后封装刚刚返回的数据称为一个chain链。然后直接作为结果返回就好了。

需要注意的是,在获取url与controller等的实例之间的关系时,不使用父类去挨着遍历的,而是在子类中自动注册到父类中的。所以AbstractUrlHandlerMapping中会有一个handlerMap的集合装有所以的映射关系。

 

AbstractMethodHandlerMapping这个是针对method来进行匹配的,

初始化过程中是因为实现了initilizingBean接口,然后会调用afterPropertiesSet方法,这个方法调用了initHandlerMethods方法,在里面会获取到所有的bean对象,然后又通过循环的方式调用了detectHandlerMethods(beanName)方法,这个方法会对类中的放进行选取,然后将选取到的符合条件的方法用于注册三个map,其实会用到controller和method和mappings.get(method).获取到的这个集合通过循环遍历的方式进行注册,会注册到handlerMap和urlMap和nameMap中去,到这里注册完成后基本上完成了初始化的过程。

调用的过程中实际上是调用了getHandlerInternal方法的,这个方法中会调用lookupHandlerMethod方法来对方法进行筛选,能匹配到的都放到一个集合里面去,然后对其进行排序,选取排序后的第一个作为最好的,如果还有第二个和第一个是一样的话就说明冲突了,抛出异常,然后将最好的这个匹配结果封装成methodResolver返回去,翻回去也就是返回到了getHandler中去了,那么接下来将得到的method的handler进行封装,将其封装为ExecutionChain然后返回即可完成了。



 

 

以上是关于SpringMVC源码解读--HandlerMapping代码解读的主要内容,如果未能解决你的问题,请参考以下文章

SpringMVC源码解读 - HandlerMapping

SpringMVC源码解读 - RequestMapping注解实现解读 - RequestMappingInfo

SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系

SpringMVC源码解读--HandlerMapping代码解读

SpringMVC源码解读--HandlerMapping代码解读

SpringMVC源码解读--DispatcherServlet类