目录
  1. 配置文件
  2. SimpleUrlHandlerMapping类
  3. AbstractUrlHandlerMapping类
  4. 总结
springMVC(4) HandlerMapping实现之SimpleUrlHandlerMapping

HandlerMapping在springMVC中有多种实现,SimpleUrlHandlerMapping就是其中的实现方式之一。

配置文件

首先我们来看一下SimpleUrlHandlerMapping的配置方式,它有如下两种配置方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<beans ...>

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/welcome.htm">welcomeController</prop>
<prop key="/*/welcome.htm">welcomeController</prop>
<prop key="/helloGuest.htm">helloGuestController</prop>
</props>
</property>
</bean>

<bean id="welcomeController"
class="com.mkyong.common.controller.WelcomeController" />

<bean id="helloGuestController"
class="com.mkyong.common.controller.HelloGuestController" />

</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<beans ...>

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/welcome.htm=welcomeController
/*/welcome.htm=welcomeController
/helloGuest.htm=helloGuestController
</value>
</property>
</bean>

<bean id="welcomeController"
class="com.mkyong.common.controller.WelcomeController" />

<bean id="helloGuestController"
class="com.mkyong.common.controller.HelloGuestController" />

</beans>

SimpleUrlHandlerMapping类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {

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

public void setMappings(Properties mappings) {
CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
}

public void setUrlMap(Map<String, ?> urlMap) {
this.urlMap.putAll(urlMap);
}

public Map<String, ?> getUrlMap() {
return this.urlMap;
}

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

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
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);
});
}
}

}

从源码中我们可以看出配置文件中的mappings最终都存储到了urlMap中了。

initApplicationContext()的作用就是在父类initApplicationContext的基础上再执行registerHandlers操作。

registerHandlers()作用是:1、将所有url全部前面补全/,2、对每个url及对应的handler执行registerHandler方法。

接下来我们继续看SimpleUrlHandlerMapping调用的registerHandler方法。

AbstractUrlHandlerMapping类

registerHandler是在SimpleUrlHandlerMapping的父类AbstractUrlHandlerMapping中定义的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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.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));
}
}
}
}

这部分代码主要做了如下一些事。

  1. 对于非懒加载且单例的方式进行了处理。
  2. 判断之前是否已经存在过处理该url的handler,对于冲突会抛出异常。
  3. 如果url为”/“,则设置为rootHandler;如果url为”/*”,则设置为defaultHandler;否则作为普通handler进行注册。

关于SimpleUrlHandlerMapping的register部分先到这里,接下来我们来看看里面我们最需要关注的getHandlerInternal函数,这个函数也是在AbstractUrlHandlerMapping类实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
Object 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 ("/".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 = obtainApplicationContext().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;
}

从这个函数中我们可以发现整个函数的匹配过程,首先对request进行一次普通匹配,若普通匹配不到,则判断是否为根路径,否则匹配默认handler。

我们会发现我们关于普通匹配还没有进行深入地剖析。

普通匹配的实现为lookupHandler函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 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 = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}

// Pattern match?
List<String> matchingPatterns = new ArrayList<>();
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 = obtainApplicationContext().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<>();
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;
}

从这函数中我们可以看出对于普通匹配,会优先判断是否由精确匹配,若无法精确匹配再进行模式串匹配。模式串匹配的优先级按照AntPattern的规则,取出其中的最优匹配。

总结

SimpleUrlHandlerMapping通过LinkedHashMap的方式来存储url与handler的映射关系。

整个匹配流程如下:

  1. 进行精确路径的匹配
  2. 进行模式串匹配,按照AntPattern的规则找出其中最优的匹配
  3. 进行根路径匹配
  4. 使用默认handler
文章作者: 谷河
文章链接: https://www.lyytaw.com/spring/springMVC(4)-HandlerMapping%E5%AE%9E%E7%8E%B0%E4%B9%8BSimpleUrlHandlerMapping/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 谷河|BLOG