源代码版本 : spring-webmvc-5.1.4.RELEASE
概述
SimpleUrlHandlerMapping
是HandlerMapping
接口的一个实现,用于映射URL
到请求处理器bean
实例或者bean
名称。如果是映射到bean
名称的情况,相应的bean
实例不能是单例的。
SimpleUrlHandlerMapping
有一个属性Map<String, Object> urlMap
用于接收外部指定的映射关系,比如通过XML
定义方式中的map
元素。
另外SimpleUrlHandlerMapping
提供了一个方法setMappings()
可以将一个Properties
形式的映射保存到urlMap
:
/welcome.html=ticketController
/show.html=ticketController
每个映射项的语法是 PATH=HANDLER_BEAN_NAME
,并且如果路径部分不以斜杠开头,会自动被添加一个。
SimpleUrlHandlerMapping
所管理的映射路径可以是URL
,也可以是Ant
风格带通配符的URL pattern
:
- 请求
/test
会匹配到 路径为/test
的映射项上 - 请求
/test
会匹配到 路径为/*
的映射项上
源代码解析
package org.springframework.web.servlet.handler;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.springframework.beans.BeansException;
import org.springframework.util.CollectionUtils;
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
private final Map<String, Object> urlMap = new LinkedHashMap<>();
/**
* Map URL paths to handler bean names.
*
* 将 Properties 格式的 <URL,handler bean name>映射转存到 urlMap,
* 这是典型的使用方法
*
* 既支持 URL , 也支持 Ant 风格 URL pattern
* This is the typical way of configuring this HandlerMapping.
* Supports direct URL matches and Ant-style pattern matches. For syntax
* details, see the org.springframework.util.AntPathMatcher javadoc.
* @param mappings properties with URLs as keys and bean names as values
* @see #setUrlMap
*/
public void setMappings(Properties mappings) {
CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
}
/**
*
* 将 Map 格式的 <URL,handler bean instance/name>映射保存到 urlMap,
*
*
* 既支持 URL , 也支持 Ant 风格 URL pattern
* Set a Map with URL paths as keys and handler beans (or handler bean names)
* as values. Convenient for population with bean references.
* Supports direct URL matches and Ant-style pattern matches. For syntax
* details, see the org.springframework.util.AntPathMatcher javadoc.
* @param urlMap map with URLs as keys and beans as values
* @see #setMappings
*/
public void setUrlMap(Map<String, ?> urlMap) {
this.urlMap.putAll(urlMap);
}
/**
* Allow Map access to the URL path mappings, with the option to add or
* override specific entries.
* Useful for specifying entries directly, for example via "urlMap[myKey]".
* This is particularly useful for adding or overriding entries in child
* bean definitions.
*/
public Map<String, ?> getUrlMap() {
return this.urlMap;
}
/**
* initApplicationContext()是基类定义的一个方法,在当前 HandlerMapping 被设置 ApplicationContext 属性
* 时调用该方法,该方法又调用了 registerHandlers() 用于登记 urlMap 中所保存的映射关系
* Calls the #registerHandlers method in addition to the
* superclass's initialization.
*/
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}
/**
* Register all handlers specified in the URL map for the corresponding paths.
* @param urlMap a Map with URL paths as keys and handler beans or bean names as values
* @throws BeansException if a handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.trace("No patterns in " + formatMappingName());
}
else {
// 遍历 urlMap 中的每一个 entry : <url, handler>
urlMap.forEach((url, handler) -> {
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
// 如果url不以斜杠开头,自动添加一个斜杠
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());
}
}
}
}
AbstractUrlHandlerMapping
接下来我们看AbstractUrlHandlerMapping
中跟理解SimpleUrlHandlerMapping
紧密相关的一些方法。
registerHandler
/**
* 登记给定的 URL(或者URL pattern) 和指定的 请求处理器 handler 为一个映射项,
* 这里 handler 可能是对象,也可能是 handler bean 名称,如果是 handler bean 名称的
* 话,如果指定了尽早初始化,会根据名称获取 handler bean对象,从而映射总保存的会是
* * handler 对象
*
* Register the specified handler for the given URL path.
* @param urlPath the URL the bean should be mapped to
* @param handler the handler instance or handler bean name String
* (a bean name will automatically be resolved into the corresponding handler bean)
* @throws BeansException if the handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
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.
// 需要尽早初始化请求处理器bean的情况
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) {
// 将同一个 url/url pattern 映射到多个请求处理器bean的情况是不允许的,抛出异常
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 (logger.isTraceEnabled()) {
logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}