Spring MVC HandlerMapping : SimpleUrlHandlerMapping 源代码解析

介绍Spring MVC框架中的SimpleUrlHandlerMapping组件,该组件负责将URL映射到控制器。它支持直接URL匹配和Ant风格的通配符模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源代码版本 : spring-webmvc-5.1.4.RELEASE

概述

SimpleUrlHandlerMappingHandlerMapping接口的一个实现,用于映射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));
				}
			}
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值