SpringFramework之mvc的HandlerMapping

    Spring的版本5.0.9.releasehtml

    HandlerMapping是個接口,以下List-1所示:java

    List-1git

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

    咱們從DispatcherServlet的doDispatch方法提及,以下List-2,getHandler(processedRequest)會調用RequestMappingHandlerMapping實例的getHandler方法。github

    List-2web

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

     

                                                                            圖1spring

    如上面圖1所示,RequestMappingHandlerMapping的類繼承圖,List-2中getHandler方法調用在AbstractHandlerMapping中,以下List-3,接着來看getHandlerInternal(request)的實現app

    List-3ide

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所示ui

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

    List-3this

@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

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

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
相關文章
相關標籤/搜索