1. 程式人生 > >SpringMVC之HandlerMapping

SpringMVC之HandlerMapping

AbstractHandlerMapping是HandlerMapping的抽象實現,所有HandlerMapping都繼承自AbstractHandlerMapping。AbstractHandlerMapping採用模板模式設計了HandlerMapping實現的整體介面,它繼承了WebApplicationObjectSupport,初始化時會自動呼叫模板方法initApplicationContext:

protected void initApplicationContext() throws BeansException {
		extendInterceptors(this.interceptors);
		detectMappedInterceptors(this.mappedInterceptors);
		initInterceptors();
	}

extendInterceptors:用於給子類提供一個新增Interceptors的入口
detectMappedInterceptors:用於將SpringMVC容器以及父容器中所有MappedInterceptor型別的Bean新增到mappedInterceptors屬性:

protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) {
		mappedInterceptors.addAll(
				BeanFactoryUtils.beansOfTypeIncludingAncestors(
						getApplicationContext(), MappedInterceptor.class, true, false).values());
	}

initInterceptors:用來初始化Interceptor

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

具體內容就是將interceptors屬性裡面的物件分類新增到兩個集合中
Interceptors:用於配置SpringMVC的攔截器。Interceptors並不會直接使用,而是通過initInterceptors方法按型別分配到mappedInterceptors和adaptedInterceptors再使用
mappedInterceptors:此類Interceptor在使用時會與請求的url進行匹配
adaptedInterceptors:這種型別的Interceptor不需要進行匹配,在getHandler中會全部新增到返回值HandlerExecutionChain裡面
HandlerMapping通過getHandler方法獲取處理器Handler和攔截器Interceptor:

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

首先會通過getHandlerInternal根據request獲取Handler,然後呼叫getHandlerExecutionChain返回一個HandlerExecutionChain:

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		//建立一個HandlerExecutionChain 
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
		//新增所有的adaptedInterceptors
		chain.addInterceptors(getAdaptedInterceptors());
		//根據路徑匹配mappedInterceptor
		String lookupPath = this.u	rlPathHelper.getLookupPathForRequest(request);
		for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
			if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
				chain.addInterceptor(mappedInterceptor.getInterceptor());
			}
		}

		return chain;
	}

在這個方法中就是先建立一個HandlerExecutionChain,然後將匹配的攔截器都新增進去
AbstractUrlHandlerMapping
它是將url與對應的Handler儲存在一個Map中,通過getHandlerInternal返回具體的Handler

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
		//獲取請求的url
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		//根據url獲取Handler
		Object handler = lookupHandler(lookupPath, request);
		if (handler == null) {
			// 根據不同的情況獲取Handler
			
			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 = 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;
	}

下面看一下查詢的過程:

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
		// 首先直接嘗試根據url獲取handler
		Object handler = this.handlerMap.get(urlPath);
		if (handler != null) {
			// 如果不為空,那麼封裝好後直接返回
			if (handler instanceof String) {
				String handlerName = (String) handler;
				handler = getApplicationContext().getBean(handlerName);
			}
			validateHandler(handler, request);
			return buildPathExposingHandler(handler, urlPath, urlPath, null);
		}
		// 可能是萬用字元匹配的url
		List<String> matchingPatterns = new ArrayList<String>();
		//將所有匹配的url新增到集合中
		for (String registeredPattern : this.handlerMap.keySet()) {
			if (getPathMatcher().match(registeredPattern, urlPath)) {
				matchingPatterns.add(registeredPattern);
			}
		}
		//將獲取到的url排序,然後選擇第一個
		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
			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);

			//可能有多個排名第一個的handler
			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;
	}

在這個方法中有兩個主線,一個是直接查詢,找到了直接返回,另一個是通過萬用字元匹配,匹配出來可能有好多個, 需要對他們進行排序選在最優的那個:

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

在這個方法裡面註冊了兩個攔截器PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor。
下面看一下Map的初始化,初始化一共有兩個方法,一個是註冊url和Handler的名字,另一個就是註冊url和具體的Handler:

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;

		// 如果沒有設定懶載入並且型別是String,直接從容器中獲取,並新增到Map中
		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);
			}
			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));
				}
			}
		}
	}

下面看一個它的子類SimpleUrlHandlerMapping,他重寫了initApplicationContext方法:

public void initApplicationContext() throws BeansException {
		super.initApplicationContext();
		registerHandlers(this.urlMap);
	}

首先呼叫了父類的initApplicationContext方法,然後將自己Map裡面的Handler註冊到父類的Map中

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也是AbstractUrlHandlerMapping的子類,也是通過重寫initApplicationContext註冊Handler:

public void initApplicationContext() throws ApplicationContextException {
		super.initApplicationContext();
		detectHandlers();
	}
protected void detectHandlers() throws BeansException {
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
		}
		//獲取容器中所有的beanName
		String[] beanNames = (this.detectHandlersInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
				getApplicationContext().getBeanNamesForType(Object.class));

		//匹配所有的beanName
		for (String beanName : beanNames) {
			String[] urls = determineUrlsForHandler(beanName);
			if (!ObjectUtils.isEmpty(urls)) {
				// 註冊到父類的Map中
				registerHandler(urls, beanName);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
				}
			}
		}
	}

通過detectHandlers方法, 根據配置的detectHandlerInAncestorContexts引數從SpringMVC容器或者SpringMVC及其父容器中找到所有bean的beanName,然後用determineUrlsForHandler方法對每個beanName解析出對應的urls,如果解析結果不為空則將解析出的urls個beanName註冊到父類的Map。
AbstractHandlerMapping的另一個分支就是AbstractHandlerMethodMapping,它是將Method作為Handler來使用,就是我們用@RequestMapping註釋的方法

protected void initHandlerMethods() {
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for request mappings in application context: " + getApplicationContext());
		}
		//獲取所有的beanName
		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))){
				//將符合條件的beanName進行處理
				detectHandlerMethods(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}

首先拿到容器中的所有bean,然後根據一定的規則篩選出Handler,最後儲存到Map裡面,篩選使用的方法是isHandler,就是判斷是否有Controller或者RequestMapping註釋

protected void detectHandlerMethods(final Object handler) {
		//獲取當前Handler的class物件
		Class<?> handlerType =
				(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());

		// 用於儲存方法和條件
		final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
		final Class<?> userType = ClassUtils.getUserClass(handlerType);
		//選擇所有符合過濾條件的method
		Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
			@Override
			public boolean matches(Method method) {
				//獲取method的匹配條件
				T mapping = getMappingForMethod(method, userType);
				if (mapping != null) {
					mappings.put(method, mapping);
					return true;
				}
				else {
					return false;
				}
			}
		});
		//註冊到Map中
		for (Method method : methods) {
			registerHandlerMethod(handler, method, mappings.get(method));
		}
	}

在這個方法中做了兩件事:
1、從傳入的處理器中找到符合要求的方法
2、註冊

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		RequestMappingInfo info = null;
		//獲取方法上的RequestMapping的註釋
		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註釋

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		//封裝handler和當前method
		HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
		//獲取當前匹配條件對應的HandlerMethod 
		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.");
		}
		//將新的HandlerMethod 新增到map中
		this.handlerMethods.put(mapping, newHandlerMethod);
		if (logger.isInfoEnabled()) {
			logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
		}
		//獲取匹配條件集合
		Set<String> patterns = getMappingPathPatterns(mapping);
		for (String pattern : patterns) {
			//新增到map中
			if (!getPathMatcher().isPattern(pattern)) {
				this.urlMap.add(pattern, mapping);
			}
		}
		//將name和HandlerMethod 新增到Map中
		if (this.namingStrategy != null) {
			String name = this.namingStrategy.getName(newHandlerMethod, mapping);
			updateNameMap(name, newHandlerMethod);
		}
	}

下面看一下是如何找到對應的處理器的:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {		
		//獲得url
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		//從map中找到對應的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 + "]");
			}
		}
		//如果找到了建立要給新的HandlerMethod返回
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}

我們看這個方法裡面一共做了三件事:
1、獲取url
2、獲取對應的HandlerMethod
3、如果找到了建立一個新的HandlerMethod返回

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<Match>();
		//從Map中找到當前url對應的所有過濾條件
		List<T> directPathMatches = this.urlMap.get(lookupPath);
		//將條件新增到matches中
		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 {
			return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
		}
	}