SpringMVC常见组件之HandlerMapping分析

在Spring MVC 3.1 之前的版本中,Spring默认使用 DefaultAnnotationHandlerMappingAnnotationMethodHandlerAdapter来处理 @RequestMapping注解和请求方法调用,而从3.1开始提供了一组新的API,RequestMappingHandlerMappingRequestMappingHandlerAdapter完成这些工作。

HandlerMapping 叫做处理器映射器,它的作用就是根据当前 request 找到对应的 Handler 和 Interceptor,然后封装成一个 HandlerExecutionChain 对象返回

其提供了一个方法HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;供子类实现。 我们这里先看下关于请求映射处理的家族树图示:
在这里插入图片描述
从上图可以看到,请求映射的家族从该类主要分为三支:根据方法进行匹配的AbstractHandlerMethodMappingRouterFunctionMapping以及根据请求URL进行批的AbstractUrlHandlerMapping。其中RouterFunctionMapping是5.2版本后引入的,这里暂不分析。

【1】 AbstractHandlerMapping

AbstractHandlerMapping如下所示,其提供了系列模板方法以及抽象方法。

//  返回HandlerInterceptor数组
protected final HandlerInterceptor[] getAdaptedInterceptors() {}

// 返回 HandlerExecutionChain 其包括handler HandlerInterceptor
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {}

// 返回所有配置的MappedInterceptor
protected final MappedInterceptor[] getMappedInterceptors() {}

// 抽象方法供子类实现 ,返回一个handler
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

AbstractHandlerMapping 间接继承自 ApplicationObjectSupport,并重写了 initApplicationContext 方法(其实该方法也是一个模版方法),这也是 AbstractHandlerMapping 的初始化入口方法,该方法主要就是扫描容器中的拦截器然后放到全局的private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();变量中。

因为ApplicationObjectSupport实现了ApplicationContextAware接口,故而在bean的实例化过程中,凡是实现了ApplicationContextAware接口的类都会被调用其setApplicationContext方法。ApplicationObjectSupportsetApplicationContext又提供了initApplicationContext方法供子类实现。也就是说ApplicationObjectSupport的子类实例化时会被调用initApplicationContext方法。

如下方法会在AbstractHandlerMapping的子类如SimpleUrlHandlerMappingBeanNameUrlHandlerMappingRequestMappingHandlerMapping等实例化时被调用。

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

① 核心方法getHandler

再看下核心方法public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {}

@Override
@Nullable
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);
	}
	// 根据handler request获取HandlerExecutionChain 
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

	if (logger.isTraceEnabled()) {
		logger.trace("Mapped to " + handler);
	}
	else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
		logger.debug("Mapped to " + executionChain.getHandler());
	}

	if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
		CorsConfiguration config = getCorsConfiguration(handler, request);
		if (getCorsConfigurationSource() != null) {
			CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
			config = (globalConfig != null ? globalConfig.combine(config) : config);
		}
		if (config != null) {
			config.validateAllowCredentials();
		}
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}

	return executionChain;
}

方法解释如下:

  • ① 获取handler ;
  • ② 如果① 获取的handler为null,则获取一个默认的handler也就是defaultHandler;
  • ③ 如果②仍旧没有获取到handler,则返回null;
  • ④ 根据handler获取HandlerExecutionChain,其包括了handler和拦截器;
  • ⑤ 如果进行了跨域配置,则获取CorsHandlerExecutionChain

② 核心方法getHandlerExecutionChain

我们看内部的核心方法getHandlerExecutionChain

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
			(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

	for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
		if (interceptor instanceof MappedInterceptor) {
			MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
			if (mappedInterceptor.matches(request)) {
				chain.addInterceptor(mappedInterceptor.getInterceptor());
			}
		}
		else {
			chain.addInterceptor(interceptor);
		}
	}
	return chain;
}

方法解释如下:

  • ① 处理获取一个HandlerExecutionChain
  • ② 遍历adaptedInterceptors
  • ③ 如果interceptor 是 MappedInterceptor则判断是否匹配当前请求,匹配则添加到当前处理器链中
  • ④ 如果interceptor 是普通拦截器(没有指定映射URL则默认拦截所有),则直接添加到当前处理器链中

【2】 AbstractUrlHandlerMapping

该分支下是根据URL进行处理器映射匹配查找。常见实现子类有SimpleUrlHandlerMappingBeanNameUrlHandlerMapping。其覆盖了父类AbstractHandlerMappinggetHandlerInternal方法。

如下所示,其内部维护了两个final map:handlerMap 维护了一个URL与handler的映射关系;pathPatternHandlerMap 维护了一个PathPattern与handler的映射关系。当获取handler时就从这两个map中进行遍历查找。

// URL模式--handler
private final Map<String, Object> handlerMap = new LinkedHashMap<>();

// PathPattern模式--handler
private final Map<PathPattern, Object> pathPatternHandlerMap = new LinkedHashMap<>();

① 核心方法getHandlerInternal

getHandlerInternal方法会根据request获取一个handler。

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
	String lookupPath = initLookupPath(request);
	Object handler;
	if (usesPathPatterns()) {
		RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
		handler = lookupHandler(path, lookupPath, request);
	}
	else {
		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 (StringUtils.matchesCharacter(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 = obtainApplicationContext().getBean(handlerName);
			}
			// 这里默认是空方法
			validateHandler(rawHandler, request);
			handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
		}
	}
	return handler;
}

方法解释如下:

  • ① 根据request获取lookupPath;
  • ② 如果patternParser不为null,调用lookupHandler(path, lookupPath, request)也就是从pathPatternHandlerMap寻找handler;
  • ③ 如果patternParser为null,则调用lookupHandler(lookupPath, request)也就是从handlerMap进行查找;
  • ④ 如果②③之后handler还为null,则尝试获取RootHandler、DefaultHandler。
    • ⑤ 如果找到的 Handler 是一个 String,则去 Spring 容器中查找该 String 对应的 Bean;
    • ⑥ 调用 validateHandler 方法来校验找到的 handler 和 request 是否匹配,这是一个空方法,可以忽略。
    • ⑦ 通过 buildPathExposingHandler 方法将找到的handler包装为HandlerExecutionChain然后添加PathExposingHandlerInterceptor拦截器,并根据uriTemplateVariables是否为空添加UriTemplateVariablesHandlerInterceptor拦截器。

② 核心方法lookupHandler(String lookupPath, HttpServletRequest request)

也就是根据URL获取一个handler。

@Nullable
protected Object lookupHandler(String lookupPath, HttpServletRequest request) throws Exception {
	Object handler = getDirectMatch(lookupPath, request);
	if (handler != null) {
		return handler;
	}

	// Pattern match? 也就是通配符匹配
	List<String> matchingPatterns = new ArrayList<>();
	for (String registeredPattern : this.handlerMap.keySet()) {
		if (getPathMatcher().match(registeredPattern, lookupPath)) {
			matchingPatterns.add(registeredPattern);
		}
		else if (useTrailingSlashMatch()) {
			if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", lookupPath)) {
				matchingPatterns.add(registeredPattern + "/");
			}
		}
	}

	String bestMatch = null;
	// AntPathMatcher.AntPatternComparator
	Comparator<String> patternComparator = getPathMatcher().getPatternComparator(lookupPath);
	if (!matchingPatterns.isEmpty()) {
		matchingPatterns.sort(patternComparator);
		if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
			logger.trace("Matching patterns " + 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 = obtainApplicationContext().getBean(handlerName);
		}
		validateHandler(handler, request);
		String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, lookupPath);

		// 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<>();
		for (String matchingPattern : matchingPatterns) {
			if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
				Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, lookupPath);
				Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
				uriTemplateVariables.putAll(decodedVars);
			}
		}
		if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
			logger.trace("URI variables " + uriTemplateVariables);
		}
		return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
	}

	// No handler found...
	return null;
}

方法解释如下:

  • ① 根据lookupPath从handlerMap中获取对应的handler,如果存在则直接返回。注意,这里是直接匹配,也就是没有通配符
  • ② 遍历handlerMap:
    • ③ 如果当前key与lookupPath匹配(由AntPathMatcher决定),则添加当前key到List<String> matchingPatterns中;
    • ④ 如果当前key+"/" 与lookupPath匹配(由AntPathMatcher决定),则添加当前key+"/"List<String> matchingPatterns中;
  • ⑤ 根据PathMatcher获取PatternComparator,默认是AntPatternComparator
  • ⑥ 如果matchingPatterns不为空,则根据PatternComparator进行排序然后获取第一个元素赋予bestMatch;
  • ⑦ 如果获取的bestMatch不为空则从handlerMap中获取对应的handler;
    • ⑧ 如果⑦获取的handler为空且bestMatch.endsWith("/"),则bestMatch去掉"/"再次从handlerMap获取handler;
    • ⑨ 如果⑧获取的handler仍旧为null,则抛出异常。
    • (10) 如果找到的 handler 是 String 类型的,则再去 Spring 容器中查找对应的 Bean,接下来再调用 validateHandler 方法进行验证;
    • (11) extractPathWithinPattern 方法提取出匹配部分pattern-matched路径。例如定义的接口规则是 myroot/*.html,请求路径是 myroot/myfile.html,那么最终获取到的就是 myfile.html
    • (12) 遍历matchingPatterns,然后patternComparator.compare(bestMatch, matchingPattern) == 0 也就是判断当前matchingPattern是否与bestMatch等价。然后根据每一个matchingPatternlookupPath尝试解析path里面变量放入Map<String, String> uriTemplateVariables
    • (13) 调用buildPathExposingHandler,这里不再赘述。
  • (14) 如果获取的bestMatch为空,则直接返回null。

由于一个请求 可能会和定义的多个接口匹配上(接口定义的URL带有通配符),所以 matchingPatterns 变量是一个数组。接下来就要对 matchingPatterns 进行排序,排序完成后,选择排序后的第一项作为最佳选项赋值给 bestMatch 变量。默认的排序规则是 AntPatternComparator,当然开发者也可以自定义。AntPatternComparator 中定义的优先级如下:

URL优先级
不含任何特殊符号的路径,如:/a/b/c第一优先级
带有{}的路径,如:/a/{b}/c第二优先级
带有正则的路径,如:/a/{regex:\d{3}}/c第三优先级
带有*的路径,如:/a/b/*第四优先级
带有**的路径,如:/a/b/**第五优先级
最模糊的匹配:/**最低优先级

③ 核心方法registerHandler

该方法就是扫描并注册handler,在bean实例化时被调用,如下所示。其中detectHandlers会调用registerHandler(urls, beanName)进行遍历循环注册。

@Override
public void initApplicationContext() throws ApplicationContextException {
	super.initApplicationContext();
	detectHandlers();
}

这里我们直接看registerHandler(urlPath, beanName)方法,其尝试将urlPath = resolvedHandler放入handlerMap中。

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;
			ApplicationContext applicationContext = obtainApplicationContext();
			if (applicationContext.isSingleton(handlerName)) {
				resolvedHandler = applicationContext.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.isTraceEnabled()) {
					logger.trace("Root mapping to " + getHandlerDescription(handler));
				}
				setRootHandler(resolvedHandler);
			}
			else if (urlPath.equals("/*")) {
				if (logger.isTraceEnabled()) {
					logger.trace("Default mapping to " + getHandlerDescription(handler));
				}
				setDefaultHandler(resolvedHandler);
			}
			else {
				this.handlerMap.put(urlPath, resolvedHandler);
				if (getPatternParser() != null) {
					this.pathPatternHandlerMap.put(getPatternParser().parse(urlPath), resolvedHandler);
				}
				if (logger.isTraceEnabled()) {
					logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
				}
			}
		}
	}

方法源码解释如下:

  • Object resolvedHandler = handler,如果!this.lazyInitHandlers && handler instanceof String,则尝试从Spring容器中获取resolvedHandler;
  • ② 根据urlPath尝试从handlerMap获取handler,如果handler不为空且不等于入参中的handler则抛出异常。如果不为空且等于入参中的handler,则表示已经存在不做处理。
  • ③ 如果从handlerMap获取不到handler:
    • ④ 如果urlPath.equals("/"),则将resolvedHandler设置为RootHandler
    • urlPath.equals("/*"),则将resolvedHandler设置为DefaultHandler
    • this.handlerMap.put(urlPath, resolvedHandler);将其放入handlerMap。

【3】SimpleUrlHandlerMapping

顾名思义,就是在URL与实例bean(或bean 名称)维护映射关系。其内部维护了一个final mapprivate final Map<String, Object> urlMap = new LinkedHashMap<>();

其构造方法如下所示:

private final Map<String, Object> urlMap = new LinkedHashMap<>();

// 空的构造函数
public SimpleUrlHandlerMapping() {
}
//根据urlMap 初始化  final Map<String, Object> urlMap
public SimpleUrlHandlerMapping(Map<String, ?> urlMap) {
	setUrlMap(urlMap);
}
// 在前者的基础上并设置当前handleMapping的order
public SimpleUrlHandlerMapping(Map<String, ?> urlMap, int order) {
	setUrlMap(urlMap);
	setOrder(order);
}

其重写了父类的initApplicationContext方法,在该方法中调用了父类initApplicationContext方法,并调用了registerHandlers(this.urlMap);

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

① 核心方法registerHandlers

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
	if (urlMap.isEmpty()) {
		logger.trace("No patterns in " + formatMappingName());
	}
	else {
		urlMap.forEach((url, handler) -> {
			// 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);
		});
		if (logger.isDebugEnabled()) {
			List<String> patterns = new ArrayList<>();
			if (getRootHandler() != null) {
				patterns.add("/");
			}
			if (getDefaultHandler() != null) {
				patterns.add("/**");
			}
			patterns.addAll(getHandlerMap().keySet());
			logger.debug("Patterns " + patterns + " in " + formatMappingName());
		}
	}
}

方法解释如下:

  • ① 如果urlMap为空,则直接返回;
  • ② 遍历循环urlMap,如果当前URL不是以"/"开头,则拼接前缀"/";如果handler是string,则进行trim;然后调用父类AbstractUrlHandlerMapping的registerHandler方法。

② SimpleUrlHandlerMapping的使用

一个应用实例是如下配置,则这里urlMap就维护了映射关系。

  <mvc:view-controller path="/" view-name="index"></mvc:view-controller>

在这里插入图片描述
也就是说,如果你想使用SimpleUrlHandlerMapping,那么请自行配置映射关系。如下xml配置:

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
     <property name="urlMap">
         <map>
             <entry key="/hello" value="helloController"/>
         </map>
     </property>
     <!--或者如下配置-->
     <property name="mappings">
      	<props>
             <prop key="/hello">helloController</prop>
         </props>
     </property>
     <!--或者如下配置-->
     <property name="mappings">
      	<value>
		   /hello=helloController
		</value>
     </property>
 </bean>

helloController是controller的名字,需要实现Controller接口,如下所示:

@org.springframework.stereotype.Controller
public class HelloController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println(request.getRequestURI());
        System.out.println(this.getClass().getName());
        ModelAndView modelAndView=new ModelAndView();
        modelAndView.setViewName("success");
        return modelAndView;
    }
}

如果启用了defaultServletHandler那么SimpleUrlHandlerMappingurlMap中还有如下配置:

/**=org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0

在这里插入图片描述

【4】AbstractDetectingUrlHandlerMapping

这个简单来讲,就是扫描spring容器中注册的bean。如果bean name或bean的别名是以"/"开头,那么就将bean name或bean的别名作为URL,调用父类AbstractUrlHandlerMapping的registerHandler(String[] urlPaths, String beanName)方法最后注册到handlerMap中。

我们同样从initApplicationContext方法看起,其调用了父类的AbstractHandlerMappinginitApplicationContext方法并调用detectHandlers来检测handler。

@Override
public void initApplicationContext() throws ApplicationContextException {
	super.initApplicationContext();
	detectHandlers();
}

① 核心方法detectHandlers

protected void detectHandlers() throws BeansException {
	ApplicationContext applicationContext = obtainApplicationContext();
	String[] beanNames = (this.detectHandlersInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
			applicationContext.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);
		}
	}
// ... 这里是无需关注的日志打印
}

方法解释如下:

  • ① 获取ApplicationContext;
  • ② 获取容器中的beanNames ,这里得到一个数组;
  • ③ 遍历beanNames ,检测其URL。如果URL不为空则注册到handlerMap。

在这里插入图片描述

这里determineUrlsForHandler是个抽象方法,供子类实现。其只有一个子类BeanNameUrlHandlerMapping,我们看下其实现determineUrlsForHandler方法。

@Override
protected String[] determineUrlsForHandler(String beanName) {
	List<String> urls = new ArrayList<>();
	if (beanName.startsWith("/")) {
		urls.add(beanName);
	}
	String[] aliases = obtainApplicationContext().getAliases(beanName);
	for (String alias : aliases) {
		if (alias.startsWith("/")) {
			urls.add(alias);
		}
	}
	return StringUtils.toStringArray(urls);
}

方法很简单,判断beanName是否以"/"开头,是的话放入urls数组中。然后获取beanName的别名,对别名数组进行遍历判断是否以否以"/"开头,是的话放入urls数组中。

也就是说,上面我们的HelloController可以指定名字如下所示:

@org.springframework.stereotype.Controller("/helloController")
public class HelloController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println(request.getRequestURI());
        System.out.println(this.getClass().getName());
        ModelAndView modelAndView=new ModelAndView();
        modelAndView.setViewName("success");
        return modelAndView;
    }
}

【5】AbstractHandlerMethodMapping

在前面 AbstractUrlHandlerMapping 体系下,一个 Handler 一般就是一个类(显示Controller如HelloController或者隐式controller如ParameterizableViewController)。但是在 AbstractHandlerMethodMapping 体系下,一个 Handler 通常指的是一个 Mehtod,是HandlerMethod类型。

AbstractHandlerMethodMapping主要维护了请求handlerMethod之间的映射关系。其覆盖了父类AbstractHandlerMapping如下三个方法:

// 核心方法,获取内部handler。该体系下返回handlerMethod
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {}

//如下两个方法和跨域配置有关
protected CorsConfiguration getCorsConfiguration(Object handler, HttpServletRequest request) {}
protected boolean hasCorsConfigurationSource(Object handler) {}

其提供了如下抽象方法供子类实现

//RequestMappingInfoHandlerMapping实现
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);

//RequestMappingInfoHandlerMapping实现
protected abstract Comparator<T> getMappingComparator(HttpServletRequest request);

//RequestMappingHandlerMapping实现
protected abstract boolean isHandler(Class<?> beanType);

//RequestMappingHandlerMapping实现
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);

核心变量private final MappingRegistry mappingRegistry = new MappingRegistry(); 为什么说是核心变量?我们可以看下mappingRegistry究竟是个什么?如下所示其registry 维护了所有的RequestMappingInfo实例与MappingRegistration映射关系。pathLookup 维护了directPath与List<RequestMappingInfo>实例关系。当处理请求获取handlerMethod时,qi

private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();

private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

我们看一下该体系下的家族树图示

在这里插入图片描述

需要注意的是AbstractHandlerMethodMapping其实现了InitializingBean接口,重写了afterPropertiesSet方法。为什么这里特意提一下实现了InitializingBean接口,那是因为这个接口定义了其实现Bean在容器完成属性设置后可以执行自定义初始化操作,我们的AbstractHandlerMethodMapping便实现了这个接口,并且定义了一组自定义操作,就是用来检测处理我们的@RequestMapping注解。

① afterPropertiesSet

如下图所示,如何bean实现了InitializingBean接口,那么在初始化过程中一定会调用其afterPropertiesSet方法。
在这里插入图片描述
这里我们看一下RequestMappingHandlerMapping.afterPropertiesSet方法。

@Override
@SuppressWarnings("deprecation")
public void afterPropertiesSet() {

	this.config = new RequestMappingInfo.BuilderConfiguration();
	this.config.setTrailingSlashMatch(useTrailingSlashMatch());
	this.config.setContentNegotiationManager(getContentNegotiationManager());

	if (getPatternParser() != null) {
		this.config.setPatternParser(getPatternParser());
		Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
				"Suffix pattern matching not supported with PathPatternParser.");
	}
	else {
		this.config.setSuffixPatternMatch(useSuffixPatternMatch());
		this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
		this.config.setPathMatcher(getPathMatcher());
	}

	super.afterPropertiesSet();
}

可以看到其主要做了一些配置如后缀模式匹配,然后调用父类AbstractHandlerMethodMappingafterPropertiesSet方法。其源码如下所示-在初始化过程中检测处理器方法。

@Override
public void afterPropertiesSet() {
	initHandlerMethods();
}

简单直接调用了AbstractHandlerMethodMapping#initHandlerMethods方法。如下所示父类直接调用initHandlerMethods方法进行handlerMethod的初始化注册。

@Override
public void afterPropertiesSet() {
	initHandlerMethods();
}

① initHandlerMethods

initHandlerMethods方法源码如下:

// 在ApplicationContext中扫描bean,检测并注册 handler methods
protected void initHandlerMethods() {
	for (String beanName : getCandidateBeanNames()) {
	    // 如果beanName不是以scopedTarget开头,就执行processCandidateBean
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			processCandidateBean(beanName);
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}

这里我们着重分析processCandidateBean(beanName);getHandlerMethods()方法。

② processCandidateBean

processCandidateBean(beanName);方法的作用是什么呢?判断其是否为一个handler(也就是是否拥有@Controller注解或@RequestMapping注解)。如果是一个handler,则检测并注册其内部方法。

protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
	try {
		beanType = obtainApplicationContext().getType(beanName);
	}
	catch (Throwable ex) {
		// An unresolvable bean type, probably from a lazy bean - let's ignore it.
		if (logger.isTraceEnabled()) {
			logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
		}
	}
	if (beanType != null && isHandler(beanType)) {
	    // 注意,这里是核心
		detectHandlerMethods(beanName);
	}
}

isHandler(beanType)是一个抽象方法,在子类RequestMappingHandlerMapping中实现。

我们看下isHandler(beanType)是如何判断当前bean是否为一个handler的?

@Override
protected boolean isHandler(Class<?> beanType) {
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

判断类上面的注解是否是(或拥有)@Controller或者@RequestMapping(或者复合注解如@RestController、@PostMapping)。OK,我们继续看detectHandlerMethods方法如何扫描并注册。

③ detectHandlerMethods

扫描handler内部的handler method然后注册到AbstractHandlerMethodMapping内部的常量private final MappingRegistry mappingRegistry = new MappingRegistry();中。

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

	if (handlerType != null) {
		Class<?> userType = ClassUtils.getUserClass(handlerType);
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {
						return getMappingForMethod(method, userType);
					}
					catch (Throwable ex) {
						throw new IllegalStateException("Invalid mapping on handler class [" +
								userType.getName() + "]: " + method, ex);
					}
				});
		if (logger.isTraceEnabled()) {
			logger.trace(formatMappings(userType, methods));
		}
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

这里我们暂不展开分析,放在第三部分中展开。我们只需要知道在RequestMappingHandlerMapping 初始化过程中,该方法将会扫描容器中那些Controller及内部method。获取handler、method、mapping,也就是处理器、处理器方法与请求映射信息(包括@RequestMapping标注的那些参数)。

扫描并注册完之后,到了handlerMethodsInitialized(getHandlerMethods());。这里我们看下getHandlerMethods

④ getHandlerMethods

AbstractHandlerMethodMapping#getHandlerMethods方法如下:

 // 返回只读的map  mapping :HandlerMethod
public Map<T, HandlerMethod> getHandlerMethods() {
 // private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	this.mappingRegistry.acquireReadLock();
	try {
		return Collections.unmodifiableMap(
				this.mappingRegistry.getRegistrations().entrySet().stream()
						.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().handlerMethod)));
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

方法源码解释如下:

  • 获取mappingRegistry中的读锁然后加锁;
  • 遍历MappingRegistry.registry,返回一个只读Map;
  • 释放锁

handlerMethodsInitialized(getHandlerMethods());方法则更简单,只是简单打印告诉系统已经已经有多少个mappings被扫描注册。

【6】detectHandlerMethods详细分析

detectHandlerMethods方法是该组件的核心方法,将会扫描@RequestMapping注解的方法并创建RequestMappingInfo实例。然后将将RequestMappingInfo实例以及处理器方法注册到AbstractHandlerMethodMapping的内部类MappingRegistry中常量Mapprivate final Map<T, MappingRegistration<T>> registry = new HashMap<>();

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

	if (handlerType != null) {
		Class<?> userType = ClassUtils.getUserClass(handlerType);
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {
						return getMappingForMethod(method, userType);
					}
					catch (Throwable ex) {
						throw new IllegalStateException("Invalid mapping on handler class [" +
								userType.getName() + "]: " + method, ex);
					}
				});
		if (logger.isTraceEnabled()) {
			logger.trace(formatMappings(userType, methods));
		}
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

代码解释如下:

  • getMappingForMethod方法创建RequestMappingInfo实例;
  • methods.forEach方法循环遍历并注册handlerinvocableMethodmapping到内存中。

这里我们可以先看下method、mapping的实例
在这里插入图片描述
如上图所示,method记录了方法详细信息诸如声明类、名字、返回结果类型、参数类型、异常类型、修饰符、注解、参数、是否覆盖等等。mapping则记录了@RequestMapping注解里面的信息如URL、请求方法、参数限制、header、consume、produce以及自定义限制条件等。

① getMappingForMethod

@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		if (typeInfo != null) {
			info = typeInfo.combine(info);
		}
		String prefix = getPathPrefix(handlerType);
		if (prefix != null) {
			info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
		}
	}
	return info;
}

代码解释如下:

  • ① 根据method创建RequestMappingInfo 实例info
  • ② 根据handlerType创建RequestMappingInfo 实例实例typeInfo
  • ③ 如果②中创建的实例typeInfo 不为null,则info = typeInfo.combine(info)进行合并;
  • ④ 根据handlerType获取类上面定义的路径前缀,如果前缀不为空则对info进行合并。

② 创建RequestMappingInfo实例

RequestMappingHandlerMapping#createRequestMappingInfo源码如下所示:

@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// //获取方法method上的@RequestMapping实例。
	RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
// 这里始终为null	
	RequestCondition<?> condition = (element instanceof Class ?
			getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
	return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

然后我们继续看createRequestMappingInfo(requestMapping, condition)方法。

protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

	RequestMappingInfo.Builder builder = RequestMappingInfo
			.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
			.methods(requestMapping.method())
			.params(requestMapping.params())
			.headers(requestMapping.headers())
			.consumes(requestMapping.consumes())
			.produces(requestMapping.produces())
			.mappingName(requestMapping.name());
	if (customCondition != null) {
		builder.customCondition(customCondition);
	}
	return builder.options(this.config).build();
}

看着是不是会熟悉?其实就是获取@RequestMapping注解的url、params、headers、consumes、produces以及name最后进行创建RequestMappingInfo实例。

关于@RequestMapping注解的url、params、headers、consumes、produces以及name这些本文不做赘述,更多信息参考博文SpringMVC-@RequestMapping的参数和用法

@RequestMapping注解信息实例

@org.springframework.web.bind.annotation.RequestMapping(consumes=[], headers=[], method=[], name=, params=[], path=[/**/testInterceptor], produces=[], value=[/**/testInterceptor])

RequestMappingInfo实例
在这里插入图片描述

③ 类与方法上面的请求映射实例合并typeInfo.combine(info)

这里我们着重看一下根据类创建的RequestMappingInfo实例与根据方法创建的RequestMappingInfo实例合并过程。

@Override
public RequestMappingInfo combine(RequestMappingInfo other) {
	String name = combineNames(other);

	PathPatternsRequestCondition pathPatterns =
			(this.pathPatternsCondition != null && other.pathPatternsCondition != null ?
					this.pathPatternsCondition.combine(other.pathPatternsCondition) : null);

	PatternsRequestCondition patterns =
			(this.patternsCondition != null && other.patternsCondition != null ?
					this.patternsCondition.combine(other.patternsCondition) : null);

	RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
	ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
	HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
	ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
	ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
	RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);

	return new RequestMappingInfo(
			name, pathPatterns, patterns, methods, params, headers, consumes, produces, custom);
}

这里涉及到了几个类,我们大致了解下含义:

  • PatternRequestCondition 它其实就是URL模式的封装,它包含了一个URL模式的Set集合。其实就是@RequestMapping注解中的value值得封装。

  • RequestMethodRequestCondition 它是@RequestMapping 注解中method属性的封装

  • ParamsRequestCondition 它是@RequestMapping注解中params属性的封装

等等,依次类推。因此RequestMappingInfo其实就是对@RquestMapping 的封装。这里关于method、params、heade等的合并我们暂时不理会(其实你对框架应用熟悉就应该能想到这些合并就是做条件与),着重看下patternsCondition的合并。

@Override
public PatternsRequestCondition combine(PatternsRequestCondition other) {
	if (isEmptyPathMapping() && other.isEmptyPathMapping()) {
		return this;
	}
	else if (other.isEmptyPathMapping()) {
		return this;
	}
	else if (isEmptyPathMapping()) {
		return other;
	}
	Set<String> result = new LinkedHashSet<>();
	if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) {
		for (String pattern1 : this.patterns) {
			for (String pattern2 : other.patterns) {
				result.add(this.pathMatcher.combine(pattern1, pattern2));
			}
		}
	}
	return new PatternsRequestCondition(result, this);
}

代码解释如下:

  • 如果两个path都为空,则返回this;
  • 如果其中一个为空,返回不为空的那个;
  • 如果两个都不为空,则进行笛卡尔积合并;

两个pattern如何合并的呢?url拼接是由PathMatcher来完成的,我们看下实例

pattern1pattern2结果
/*/hotel/hotel
/*.*/*.html/*.html
/usr/user/usr/user
/{foo}/bar/{foo}/bar
/hotels/*/booking/hotels/booking
/hotels/*booking/hotels/booking
/hotels/booking/hotels/booking
/hotelsbooking/hotels/booking
/*.html/hotel/hotel.html
/*.html/hotel.*/hotel.html

④ 注册HandlerMethod

从上面看到这里会进行循环然后进行注册,如下所示:

methods.forEach((method, mapping) -> {
	Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
	registerHandlerMethod(handler, invocableMethod, mapping);
});

AbstractHandlerMethodMapping#registerHandlerMethod方法如下:

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
	this.mappingRegistry.register(mapping, handler, method);
}

继续跟进AbstractHandlerMethodMapping.MappingRegistry#register

public void register(T mapping, Object handler, Method method) {
	this.readWriteLock.writeLock().lock();
	try {
		HandlerMethod handlerMethod = createHandlerMethod(handler, method);
		// 这里mapping就是RequestMappingInfo实例 
		validateMethodMapping(handlerMethod, mapping);
		// 获取请求URL	,然后往pathLookup放入path与mapping
		Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
		for (String path : directPaths) {
			this.pathLookup.add(path, mapping);
		}
		// 获取name,然后往nameLookup中放入name=List<HandlerMethod>
		String name = null;
		if (getNamingStrategy() != null) {
			name = getNamingStrategy().getName(handlerMethod, mapping);
			addMappingName(name, handlerMethod);
		}

		CorsConfiguration config = initCorsConfiguration(handler, method, mapping);
		if (config != null) {
			config.validateAllowCredentials();
			this.corsLookup.put(handlerMethod, config);
		}

		this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name));
	}
	finally {
		this.readWriteLock.writeLock().unlock();
	}
}

代码解释如下:

  • ① 获取写锁进行加锁;

  • ② 获取HandlerMethod;
    在这里插入图片描述

  • ③ 校验是否存在不同的HandlerMethod对应同一个RequestMappingInfo实例。简单来说就是同样的@RequestMapping注解在不同的类或者方法上。

  • ④ 获取映射URL,然后放到AbstractHandlerMethodMapping内部类MappingRegistryprivate final MultiValueMap<String, T> pathLookup中,path=RequestMappingInfo。这里放的URL是不带通配符*和?的,也就是所谓的directPath
    在这里插入图片描述

  • ⑤ 获取handler method的name,然后往nameLookup中放入name=List<HandlerMethod>。这里类似于一个彩蛋,如URL/test访问也可以使用方法TC#test访问。TC就是TestController的缩写。
    在这里插入图片描述

  • ⑥ 判断是否设置了跨域也就是类或者方法上是否有@CrossOrigin注解。如果设置了跨域则初始化CorsConfiguration:

    • 校验是否满足跨域条件:allowCredentials为true时allowedOrigins不能为*,不满足则会抛出异常;
    • 如果满足条件则往corsLookup放入handlerMethod=configthis.corsLookup.put(handlerMethod, config);
  • this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name));AbstractHandlerMethodMapping的内部类MappingRegistryprivate final Map<T, MappingRegistration<T>> registry = new HashMap<>();放入mapping=new MappingRegistration<>(mapping, handlerMethod, directPaths, name)
    在这里插入图片描述

  • ⑧ 获取写锁,进行锁的释放。

【7】 查找lookupHandlerMethod

AbstractHandlerMethodMapping#lookupHandlerMethod方法主要是根据lookupPath来获取一个HandlerMethod 。

① lookupHandlerMethod

@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	// 从 MappingRegistry.pathLookup中获取 RequestMappingInfo实例集合
	List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
	if (directPathMatches != null) {
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
	}
	if (!matches.isEmpty()) {
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			bestMatch = matches.get(0);
			if (logger.isTraceEnabled()) {
				logger.trace(matches.size() + " matching mappings: " + matches);
			}
			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();
				String uri = request.getRequestURI();
				throw new IllegalStateException(
						"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
			}
		}
		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
		handleMatch(bestMatch.mapping, lookupPath, request);
		return bestMatch.handlerMethod;
	}
	else {
		return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
	}
}

mappingRegistry实例图示

在这里插入图片描述

方法解释如下:

  • ① 根据lookupPath从MappingRegistry的MultiValueMap<String, T> pathLookup获取List<T> directPathMatches。如果获取到结果不为空,则找出匹配当前请求的RequestMappingInfo实例,根据RequestMappingInfo实例实例和HandlerMethod组装Match,最后放到List<Match> matches中。这里我们可以再回顾一下pathLookup实例:
    在这里插入图片描述
  • ② 如果①操作后的matches为空,则尝试从MappingRegistry.registry中获取所有匹配当前请求的数据集,然后封装为Match实例对象放到matches中。
  • ③ 如果② 之后matches不为空,则判断是否只有一条。如果数据集大于一条进行排序,bestMatch = matches.get(0);。这里会额外对第一、二条数据进行比较,如果相等则抛出异常。如果数据集只有一条,则bestMatch = matches.get(0);,继续往下处理。
    • ④ 往request中放入两个属性:
      • request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
      • .request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
    • ⑤ 返回bestMatch中的handlerMethod.
  • ⑥ 如果② 之后matches为空,则走handleNoMatch方法。该方法为空,返回null,在子类RequestMappingInfoHandlerMapping中重写了该方法可能会抛出诸如HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException、UnsatisfiedServletRequestParameterException异常。

request中放的两个属性键是:

// org.springframework.web.servlet.HandlerMapping.bestMatchingHandler
String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";

// org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

上面我们看到了如果matches数据集大于一条会获取比较器然后进行排序,其会调用RequestMappingInfo#compareTo方法进行比较。

Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);

② addMatchingMappings

这里我们再看下addMatchingMappings方法。方法如下所示,会遍历mappings集合(RequestMappingInfo实例集合),从中找到匹配当前request的RequestMappingInfo,然后封装为Match对象放到List<Match> matches中。

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
	for (T mapping : mappings) {
		T match = getMatchingMapping(mapping, request);
		if (match != null) {
			matches.add(new Match(match,
					this.mappingRegistry.getRegistrations().get(mapping).getHandlerMethod()));
		}
	}
}

③ getMatchingMapping&&getMatchingCondition

那么如何判断RequestMappingInfo与request是否匹配呢? 我们继续看下getMatchingMapping方法。该方法在RequestMappingInfoHandlerMapping子类中进行了实现。

protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
	return info.getMatchingCondition(request);
}

//RequestMappingInfo#getMatchingCondition
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
	if (methods == null) {
		return null;
	}
	ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
	if (params == null) {
		return null;
	}
	HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
	if (headers == null) {
		return null;
	}
	ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
	if (consumes == null) {
		return null;
	}
	ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
	if (produces == null) {
		return null;
	}
	PathPatternsRequestCondition pathPatterns = null;
	if (this.pathPatternsCondition != null) {
		pathPatterns = this.pathPatternsCondition.getMatchingCondition(request);
		if (pathPatterns == null) {
			return null;
		}
	}
	PatternsRequestCondition patterns = null;
	if (this.patternsCondition != null) {
		patterns = this.patternsCondition.getMatchingCondition(request);
		if (patterns == null) {
			return null;
		}
	}
	RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
	if (custom == null) {
		return null;
	}
	return new RequestMappingInfo(
			this.name, pathPatterns, patterns, methods, params, headers, consumes, produces, custom);
}

如上所示,方法会逐个对method、param、header、consume、produce、pathPattern、pattern以及自定义condition进行匹配,只有全部满足则通过新建一个RequestMappingInfo实例返回,否则返回null。

总结

  • ① 在bean实例化时扫描、注册请求URL(或者是RequestMappingInfo实例)与handler;
  • ② 请求处理时,遍历查找。如果不能直接获取或者获取为多个,则会进行比较排序然后选出最优解;
  • ③ 封装为HandlerExecutionChain返回。

【8】几个概念

RequestMappingInfo

这个类是对请求映射的一个抽象,它包含了请求路径,请求方法,请求头等信息。其实可以看做是@RequestMapping的一个对应类。其内部维护了静态内部类BuilderConfigurationDefaultBuilder以及一些封装@RequestMapping注解信息的类。

其内部常量如下所示:

// 一个逻辑分离 (' || ') 请求条件,它根据一组 URL 路径模式匹配请求。这里使用已解析的PathPatterns
private static final PathPatternsRequestCondition EMPTY_PATH_PATTERNS = new PathPatternsRequestCondition();
// Patterns 也就是请求URL,PathPatternsRequestCondition 不同,这里使用String 字符串模式
private static final PatternsRequestCondition EMPTY_PATTERNS = new PatternsRequestCondition();
// 请求限制http method
private static final RequestMethodsRequestCondition EMPTY_REQUEST_METHODS = new RequestMethodsRequestCondition();
// @RequestMapping注解中的param
private static final ParamsRequestCondition EMPTY_PARAMS = new ParamsRequestCondition();
// @RequestMapping注解中的header
private static final HeadersRequestCondition EMPTY_HEADERS = new HeadersRequestCondition();
// @RequestMapping注解中的consume
private static final ConsumesRequestCondition EMPTY_CONSUMES = new ConsumesRequestCondition();
// @RequestMapping注解中的produce
private static final ProducesRequestCondition EMPTY_PRODUCES = new ProducesRequestCondition();
// 自定义条件
private static final RequestConditionHolder EMPTY_CUSTOM = new RequestConditionHolder(null);


@Nullable
private final String name;
@Nullable
private final PathPatternsRequestCondition pathPatternsCondition;
@Nullable
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;
private final int hashCode;

简单实例图示
在这里插入图片描述

② HandlerMethod

这个类封装了处理器实例(Controller Bean)和 处理方法实例(Method)以及方法参数数组(MethodParameter[])。其内部成员变量为:

private final Object bean;
@Nullable
private final BeanFactory beanFactory;
private final Class<?> beanType;
private final Method method;
private final Method bridgedMethod;
private final MethodParameter[] parameters;
@Nullable
private HttpStatus responseStatus;
@Nullable
private String responseStatusReason;
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
private final String description;

HandlerMethod实例图示
在这里插入图片描述
其有用对Method的应用,Method示例图示如下:
在这里插入图片描述

③ AbstractHandlerMethodMapping#MappingRegistry

MappingRegistry是AbstractHandlerMethodMapping内部类,内部维护了系列final类型的Map。用来存储"映射与目标"之间的关系,如下所示:

class MappingRegistry {

		private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

		private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();

		private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

		private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

		private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//...
}		

其提供了核心方法register用来向pathLookup、nameLookup、corsLookup以及registry放入数据。unregister方法从pathLookup、nameLookup、corsLookup以及registry移除入数据。

我们可以通过下图直观感受其究竟是一个怎样的实例。

在这里插入图片描述

④ ApplicationContextAware与ApplicationObjectSupport

ApplicationContextAware接口只提供了一个方法用来为当前上下文设置ApplicationContext 。

void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

当bean实例化后,会遍历BeanPostProcessor调用其postProcessBeforeInitialization进行后置处理。如ApplicationContextAwareProcessorpostProcessBeforeInitialization会判断bean是否是EnvironmentAwareEmbeddedValueResolverAwareResourceLoaderAwareApplicationEventPublisherAwareMessageSourceAwareApplicationContextAware以及ApplicationStartupAware。如果是其中之一,则会调用对应的setXXX方法如setApplicationContext。

ApplicationObjectSupport实现了ApplicationContextAware接口setApplicationContext方法,如下图所示其提供了initApplicationContext方法供子类实现。

@Override
public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
	if (context == null && !isContextRequired()) {
		// Reset internal context state.
		this.applicationContext = null;
		this.messageSourceAccessor = null;
	}
	else if (this.applicationContext == null) {
		// Initialize with passed-in context.
		if (!requiredContextClass().isInstance(context)) {
			throw new ApplicationContextException(
					"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
		}
		this.applicationContext = context;
		this.messageSourceAccessor = new MessageSourceAccessor(context);
		// 这里是核心
		initApplicationContext(context);
	}
	else {
		// Ignore reinitialization if same context passed in.
		if (this.applicationContext != context) {
			throw new ApplicationContextException(
					"Cannot reinitialize with different application context: current one is [" +
					this.applicationContext + "], passed-in one is [" + context + "]");
		}
	}
}

在本文HandlerMapping体系中,AbstractHandlerMappingAbstractDetectingUrlHandlerMapping以及SimpleUrlHandlerMapping实现了这三个方法

故而当BeanNameUrlHandlerMapping或者SimpleUrlHandlerMapping实例化时,就会触发setApplicationContext方法,然后触发initApplicationContext方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值