前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringFramework之mvc的HandlerMapping

SpringFramework之mvc的HandlerMapping

作者头像
克虏伯
发布2019-07-01 18:05:08
4610
发布2019-07-01 18:05:08
举报

Spring的版本5.0.9.release

    HandlerMapping是个接口,如下List-1所示:

List-1

代码语言:javascript
复制
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

    我们从DispatcherServlet的doDispatch方法说起,如下List-2,getHandler(processedRequest)会调用RequestMappingHandlerMapping实例的getHandler方法。

List-2

代码语言:javascript
复制
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	HandlerExecutionChain mappedHandler = null;
...
	try {
...
			// Determine handler for the current request.
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) {
				noHandlerFound(processedRequest, response);
				return;
			}
...

                                                                            图1

    如上面图1所示,RequestMappingHandlerMapping的类继承图,List-2中getHandler方法调用在AbstractHandlerMapping中,如下List-3,接着来看getHandlerInternal(request)的实现

List-3

代码语言:javascript
复制
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;
}

    getHandlerInternal的实现在AbstractHandlerMethodMapping中,如List-3所示

  1. 获取request中的url
  2. 调用lookupHandlerMethod方法获取HandlerMethod,如List-4所示

List-3

代码语言:javascript
复制
@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);
	}
	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 + "]");
			}
		}
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

List-4

代码语言:javascript
复制
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		// No choice but to go through all mappings...
		addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
	}

	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);
		return bestMatch.handlerMethod;
	}
	else {
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}

    如List-4所示,根据url从mappingRegistry中取出对应的值,如果得到多个,那么会对结果进行排序,然后取出最匹配的那个HandlerMethod。

    回到List-3,在List-4中返回HandlerMethod后,调用getHandlerExecutionChain方法构造一个HandlerExecutionChain,读者可以去看下HandlerExecutionChain的类属性——有个Handler和List<HandlerInterceptor>。如果有MappedInterceptor,那么会根据url判断是否加入到HandlerExecutionChain,如果不是则加入到HandlerExecutionChain。由此可以看出HandlerExecutionChain中包含HandlerInterceptor和代表Controller目标方法的Handler。

List-5

代码语言:javascript
复制
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;
}

    DispatcherServlet默认情况下,使用了BeanNameUrlHandlerMapping和RequestMappingHandlerMapping,见DispatcherServlet.properties中org.springframework.web.servlet.HandlerMapping的值。不过现在基本不使用BeanNameUrlHandlerMapping了,因为它的bean的ID要以/开头,不方便。

Reference

  1. https://github.com/spring-projects/spring-framework/tree/v5.0.9.RELEASE
  2. https://www.cnblogs.com/lucas2/p/9419147.html

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Reference
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档