上篇博客讲了DispathcerServlet的流转过程以及它是如何工作的,从这篇博客开始就开始深入到DispatcherServlet的内部看看它的几个主要的组件。那么这一篇就从HandlerMapping这个组件开始学习。
HandlerMapping
首先这是一个接口,也就是可扩展。它的作用就是根据不同的请求去匹配对应的Handler,也就是根据请求匹配一个请求处理器。这个过程需要两个步骤:第一步,需要将Handler注册到HandlerMapping中;第二步,分析请求根据规则从已注册的Handler中匹配到对应的Handler,即Controller。默认情况下,SpringMvc为我们提供了几个默认的HandlerMapping的实现,通过优先级的次序决定执行的顺序。
HandlerMapping执行顺序
在基于Spring MVC的Web应用程序中,我们可以为DispatcherServlet提供多个Handler- Mapping供其使用。DispatcherServlet在选用HandlerMapping的过程中,将根据我们所指定的一系列HandlerMapping的优先级进行排序,然后优先使用优先级在前的HandlerMapping。如果当前的HandlerMapping能够返回可用的Handler,DispatcherServlet则使用当前返回的Handler进行Web请求的处理,而不再继续询问其他的HandlerMapping。否则,DispatcherServlet将继续按照各个HandlerMapping的优先级进行询问,直到获取一个可用的Handler为止。
SimpleUrlHandlerMapping
现在,通过这个实现类,我们来看看handlerMapping是如何注册和获取handler的。
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { private final Map<String, Object> urlMap = new HashMap<String, Object>(); // 通过属性配置URL到Bean名的映射 public void setMappings(Properties mappings) { CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap); } // 配置URL到Bean的映射 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 { // 对于没一个配置的URL到处理器的映射,如果URL不是以斜线(/)开头,则追加斜线开头,则注册处理器 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); } } } }
首先,它通过一个hashmap来存储请求和controller的对应关系,也就是保存注册信息。类都被Ioc容器管理者,HandlerMapping管理的是对应关系。其中key是http请求的path信息,value可以是一个字符串,或者是一个处理请求的HandlerExecutionChain,如果是String类型,则会将其视为Spring的bean名称。在HandlerMapping对象的创建中,IoC容器执行了一个容器回调方法setApplicationContext,在这个方法中调用initApplicationContext方法进行初始化,各个子类可以根据需求的不同覆写这个方法。关于handlerMap信息的注册就是在initApplicationContext方法中被执行的。
取得Handler的方法在他的父类中,源码如下:
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); //查找符合匹配规则的handler。可能的结果是HandlerExecutionChain对象或者是null Object handler = lookupHandler(lookupPath, request); //如果没有找到匹配的handler,则需要处理下default handler 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(); } //在getRootHandler和getDefaultHandler方法中,可能持有的是bean name。 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); } } //如果handler还是为空,则抛出错误。 if (handler != null && this.mappedInterceptors != null) { Set<HandlerInterceptor> mappedInterceptors = this.mappedInterceptors.getInterceptors(lookupPath, this.pathMatcher); if (!mappedInterceptors.isEmpty()) { HandlerExecutionChain chain; if (handler instanceof HandlerExecutionChain) { chain = (HandlerExecutionChain) handler; } else { chain = new HandlerExecutionChain(handler); } chain.addInterceptors(mappedInterceptors.toArray(new HandlerInterceptor[mappedInterceptors.size()])); } } if (handler != null && logger.isDebugEnabled()) { logger.debug("Mapping [" + lookupPath + "] to handler '" + handler + "'"); } else if (handler == null && logger.isTraceEnabled()) { logger.trace("No handler mapping found for [" + lookupPath + "]"); } return handler; }
接口定义
看过了这些之后我们再回头看看HandleraMapping接口的定义:
public interface HandlerMapping { String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping"; String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern"; String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping"; String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables"; String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes"; public abstract HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
眼尖的同学可能就要说了,这个接口下取得handler的方法怎么返回值是HandlerExecutionChain而不是一个handler呢?HandlerExecutionChain这个类,从名字可以直观的看得出,这个对象是一个执行链的封装。熟悉Struts2的都知道,Action对象也是被层层拦截器包装,这里可以做个类比,说明SpringMVC确实是吸收了Struts2的部分设计思想。大家可以再看看源码,太长了我就不贴了。从源码中可以看出,一个实质执行对象,还有一堆拦截器。这不就是Struts2中的实现么,SpringMVC没有避嫌,还是采用了这种封装。得到HandlerExecutionChain这个执行链(execution
chain)之后,下一步的处理将围绕其展开。至此,HandlerExecutionChain整个执行脉络也就清楚了:在真正调用其handler对象前,HandlerInterceptor接口实现类组成的数组将会被遍历,其preHandle方法会被依次调用,然后真正的handler对象将被调用。
小结:这里主要的内容就是注册和获取的两个过程,以及SpringMvc对handler的一些封装。认真的读读源码,就能很清晰的看到这个类的整个执行过程。加深对SpringMvc的执行过程的了解,感觉还是相当不错的。