SpringMVC源码分析–HandlerMappings

之前分析过 SpringMVC 中的 DispatcherServlet ,分析了 SpringMVC 处理请求的过程。但忽略了一些 DispatcherServlet 协助请求处理的组件,例如 SpringMVC 中的 HandlerMappingHandlerAdapterViewResolvers 等等。

HandlerMappings

HandlerMappingsDispathServlet 中主要作用是为请求的 urlpath 匹配对应的 Controller ,建立一个映射关系,根据请求查找 HandlerInterceptorHandlerMappings 将请求传递到 HandlerExecutionChain 上, HandlerExecutionChain 包含了一个能够处理该请求的处理器,还可以包含拦截改请求的拦截器。

在没有处理器映射相关配置情况下, DispatcherServlet 会为你创建一个 BeanNameUrlHandlerMapping 作为默认映射的配置。在 DispatchServlet.properties 文件中对于 HandlerMapping 的默认配置是:

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,/
	org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

HandlerMapping 的配置策略一般分为配置式 BeanNameUrlHandlerMapping 和注解式 DefaultAnnotationHandlerMapping 。不过 DefaultAnnotationHandlerMapping 已经被放弃了,取代它的是 RequestMappingHandlerMapping ,不知道为啥 SpringMVC 这个默认配置尚未做修改。

AbstractHandlerMapping

SpringMVC源码分析--HandlerMappings

AbstractHandlerMappingHandlerMapping 的抽象实现,是所有 HandlerMapping 实现类的父类。

AbstractHandlerMapping 的作用是是为了初始化 InterceptorsAbstractHandlerMapping 重写了 WebApplicationObjectSupportinitApplicationContext 方法。

protected void initApplicationContext()throwsBeansException{
	extendInterceptors(this.interceptors);
	detectMappedInterceptors(this.adaptedInterceptors);
	initInterceptors();
}
  • extendInterceptors 方法, Springmvc 并没有做出具体实现,这里留下一个拓展,子类可以重写这个模板方法,为子类添加或者修改 Interceptors

  • detectMappedInterceptors 方法将 SpringMVC 容器中所有 MappedInterceptor 类的 bean 添加到 adaptedInterceptors 中。

  • 最后调用 initInterceptors 初始化拦截器。遍历 interceptorsWebRequestInterceptorHandlerInterceptor 类型的拦截器添加到 adaptedInterceptors 中。

HandlerMapping 通过 getHandler 方法来获取请求的处理器 Handler 和拦截器 Interceptor 。在 getHandlerExecutionChain 方法中将遍历之前初始化的 adaptedInterceptors ,为当前的请求选择对应的 MappedInterceptorsadaptedInterceptors

AbstractUrlHandlerMapping

AbstractUrlHandlerMapping

SpringMVC源码分析--HandlerMappings

AbstractUrlHandlerMapping 继承于 AbstractHandlerMapping ,它是通过 URL 来匹配具体的 HandlerAbstractUrlHandlerMapping 维护一个 handlerMap 来存储 UrlHandler 的映射关系。

AbstractUrlHandlerMapping 重写了 AbstractHandlerMapping 类中的 getHandlerInternal 方法。 HandlerMapping 通过 getHandler 方法,就会调用这里的 getHandlerInternal 方法来获取 HandlergetHandlerInternal 方法中关键调用 lookupHandler 方法去获取 handler

protectedObjectlookupHandler(String urlPath, HttpServletRequest request)throwsException{
	// 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);
		}
		else if (useTrailingSlashMatch()) {
			if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
				matchingPatterns.add(registeredPattern +"/");
			}
		}
	}

	String bestMatch = null;
	Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
	if (!matchingPatterns.isEmpty()) {
		Collections.sort(matchingPatterns, patternComparator);
		if (logger.isDebugEnabled()) {
			logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
		}
		bestMatch = matchingPatterns.get(0);
	}
	if (bestMatch != null) {
		handler = this.handlerMap.get(bestMatch);
		if (handler == null) {
			if (bestMatch.endsWith("/")) {
				handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
			}
			if (handler == null) {
				throw new IllegalStateException(
						"Could not find handler for best pattern match [" + bestMatch + "]");
			}
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = getApplicationContext().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<String, String>();
		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;
}
  • 首先调用 lookupHandler 方法来获取 handler 。在 lookupHandler 方法中,先通过 URLhandlerMap 查找是否有合适的 handler
  • 如果没有获取到 handler ,遍历 handlerMap 利用正则匹配的方法,找到符合要求的 handlers (有可能是多个)。
  • 正则匹配是采用 Ant 风格,将会通过排序筛选出一个匹配程度最高的 Handler
  • 最后调用 buildPathExposingHandler 方法构建一个 handler ,添加 PathExposingHandlerInterceptorUriTemplateVariablesHandlerInterceptor 两个拦截器并返回。

上面介绍获取 handler 的过程中,会先从 handlerMap 查找。下面看一下 handlerMap 是如何初始化的。 AbstractUrlHandlerMapping 是通过 registerHandler 初始化 handlerMap 的。 AbstractUrlHandlerMapping 共有两个 registerHandler 方法。分别是注册多个 url 到一个 handler 和注册一个 url 到一个 handler 。首先判断 handlerMap 是否有此 handler 。如果存在的话,判断是否一致,不一致则抛出异常,如果不存在的话,如果 url//* ,则,返回 root handlerdefault handler ,如果不是将添加到 handlerMap 中。

SimpleUrlHandlerMapping

SpringMVC源码分析--HandlerMappings

SimpleUrlHandlerMapping 继承于 AbstractUrlHandlerMappingSimpleUrlHandlerMapping 重写了父类 AbstractHandlerMapping 中的初始化方法 initApplicationContext 。在 initApplicationContext 方法中调用 registerHandlers 方法。

protected void registerHandlers(Map<String, Object> urlMap)throwsBeansException{
	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);
		}
	}
}

判断是 url 是否以 / 开头,如果不是,默认补齐 / ,确保所有的url都是以 / 开头,然后依次调用父类的 registerHandler 方法注册到 AbstractUrlHandlerMapping 中的 handlerMap

在使用 SimpleUrlHandlerMapping 时,需要在注册的时候配置其 urlmap 否则会抛异常。

AbstractDetectingUrlHandlerMapping

SpringMVC源码分析--HandlerMappings

AbstractDetectingUrlHandlerMapping 类继承于 AbstractUrlHandlerMapping 类,重写了 initApplicationContext 方法,在 initApplicationContext 方法中调用了 detectHandlers 方法。

protected void detectHandlers()throwsBeansException{
	if (logger.isDebugEnabled()) {
		logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
	}
	String[] beanNames = (this.detectHandlersInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
			getApplicationContext().getBeanNamesForType(Object.class));

	// Take any bean name that we can determine URLs for.
	for (String beanName : beanNames) {
		String[] urls = determineUrlsForHandler(beanName);
		if (!ObjectUtils.isEmpty(urls)) {
			// URL 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");
			}
		}
	}
}

获取所有容器的 beanNames ,遍历所有的 beanName ,调用 determineUrlsForHandler 方法解析 url ,这里的 determineUrlsForHandler 也是运用了模板方法设计模式,具体的实现在其子类中,如果解析到子类,将注册到父类的 handlerMap 中。

BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping 类的类图大致如下:

SpringMVC源码分析--HandlerMappings

BeanNameUrlHandlerMapping 类继承于 AbstractDetectingUrlHandlerMapping 类。重写了父类中的 determineUrlsForHandler 方法。

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 解析 Url 规则也很简单,判断 beanName 是否以 / 开头。

BeanNameUrlHandlerMappingSpringMVC 的默认映射配置。

AbstractHandlerMethodMapping

通常我们也习惯于用 @Controller@Re questMapping 来定义 HandlerAbstractHandlerMethodMapping 可以将 method 作为 Handler 来使用。

AbstractHandlerMethodMapping 实现了 InitializingBean 接口,实现了 afterPropertiesSet 方法。当容器启动的时候会调用 initHandlerMethods 注册委托 handler 中的方法。

public void afterPropertiesSet(){
	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)) {
			Class<?> beanType = null;
			try {
				beanType = getApplicationContext().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);
				}
			}
			if (beanType != null && isHandler(beanType)) {
				detectHandlerMethods(beanName);
			}
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}

initHandlerMethods 方法中,做了以下工作:

  • 首先通过 BeanFactoryUtils 扫描应用上下文,获取所有的 bean
  • 遍历所有的 beanName ,调用 isHandler 方法判断是目标 bean 是否包含 @Controller@RequestMapping 注解。
  • 对于带有 @Controller@RequestMapping 注解的类,调用 detectHandlerMethods 委托处理,获取所有的 method ,并调用 registerHandlerMethod 注册所有的方法。

detectHandlerMethods 方法负责将 Handler 保存到 Map 中。

protected void detectHandlerMethods(finalObject handler){
   // 获取handler的类型
	Class<?> handlerType = (handler instanceof String ?
			getApplicationContext().getType((String) handler) : handler.getClass());
	final Class<?> userType = ClassUtils.getUserClass(handlerType);
	
	Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
			new MethodIntrospector.MetadataLookup<T>() {
				@Override
				publicTinspect(Method method){
					try {
						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);
	}
	for (Map.Entry<Method, T> entry : methods.entrySet()) {
		Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
		T mapping = entry.getValue();
		registerHandlerMethod(handler, invocableMethod, mapping);
	}
}

selectMethods 方法中重写了 MetadataLookup 中的 inspect 方法, inspect 方法中调用了子类 RequestMappingHandlerMapping 实现了 getMappingForMethod 模板方法,用于构建 RequestMappingInfo

public static <T> Map<Method, T>selectMethods(Class<?> targetType,finalMetadataLookup<T> metadataLookup){
		final Map<Method, T> methodMap = new LinkedHashMap<Method, T>();
		Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
		Class<?> specificHandlerType = null;

		if (!Proxy.isProxyClass(targetType)) {
			handlerTypes.add(targetType);
			specificHandlerType = targetType;
		}
		handlerTypes.addAll(Arrays.asList(targetType.getInterfaces()));

		for (Class<?> currentHandlerType : handlerTypes) {
			final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

			ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
				@Override
				public void doWith(Method method){
					Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
					T result = metadataLookup.inspect(specificMethod);
					if (result != null) {
						Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
						if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
							methodMap.put(specificMethod, result);
						}
					}
				}
			}, ReflectionUtils.USER_DECLARED_METHODS);
		}

		return methodMap;
	}

selectMethods 通过反射获取所有的方法,重写了 doWith 方法,将 handler 中的 method 和请求对应的 RequestMappingInfo 保存到 methodMap 中。

最终 detectHandlerMethods 将遍历这个 methodMap ,调用 registerHandlerMethod 注册 HandlerMethodMappingRegistry

AbstractHandlerMethodMapping 类中,有个内部类 MappingRegistry ,用来存储 mappinghandler methods 注册关系,并提供了并发访问方法。

AbstractHandlerMethodMapping 通过 getHandlerInternal 来为一个请求选择对应的 handler

protectedHandlerMethodgetHandlerInternal(HttpServletRequest request)throwsException{
   // 根据request获取对应的urlpath
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	if (logger.isDebugEnabled()) {
		logger.debug("Looking up handler method for path " + lookupPath);
	}
	// 获取读
	this.mappingRegistry.acquireReadLock();
	try {
	  // 调用lookupHandlerMethod方法获取请求对应的HandlerMethod
		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);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

lookupHandlerMethod 的具体实现如下:

protectedHandlerMethodlookupHandlerMethod(String lookupPath, HttpServletRequest request)throwsException{
		List<Match> matches = new ArrayList<Match>();
		// 通过lookupPath获取所有匹配到的path
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
		   // 将匹配条件添加到matches
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			// 如果没有匹配条件,将所有的匹配条件都加入matches
			addMatchingMappings(this.mappingRegistry.getMappings().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) {
				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 + "}");
				}
			}
			// 将lookupPath设为请求request的PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE属性
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

整个过程以 Match 作为载体, Match 是个内部类,封装了匹配条件和 handlerMethod 两个属性,默认的实现是将 lookupPath 设置为请求的属性。

###总结

本文从源码角度上分析了 HandlerMapping 的各种实现。主要功能是为请求找到合适的 handlerinterceptors ,并组合成 HandlerExecutionChain 。查找 handler 的过程通过 getHandlerInternal 方法实现,每个子类都其不同的实现。

所有的 HandlerMapping 的实现都继承于 AbstarctHandlerMappingAbstarctHandlerMapping 主要作用是完成拦截器的初始化工作。而通过 AbstarctHandlerMapping 又衍生出两个系列, AbstractUrlHandlerMappingAbstractHandlerMethodMapping

AbstractUrlHandlerMapping 也有很多子类的实现,如 SimpleUrlHandlerMappingAbstractDetectingUrlHandlerMapping 。总体来说, AbstractUrlHandlerMapping 需要用到一个保存 urlhandler 的对应关系的 mapmap 的初始化工作由子类实现。不同的子类会有自己的策略,可以在配置文件中注册,也可以在 spring 容器中找。

AbstractHandlerMethodMapping 系列则通常用于注解的方法,解析包含 @Controller 或者 @RequestMapping 注解的类,建立 urlmethod 的直接对应关系,这也是目前使用最多的一种方式。

原文 

http://lishuo.me/2017/08/15/cj6dexenk0009fgxpco0muwyc/

PS:如果您想和业内技术大牛交流的话,请加qq群(527933790)或者关注微信公众 号(AskHarries),谢谢!

转载请注明原文出处:Harries Blog™ » SpringMVC源码分析–HandlerMappings

赞 (0)

分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址