大家好,今天我们分析下处理器映射,这个功能是MVC框架所应具备的基本功能。那么,什么是处理器映射呢,是指根据一套规则获取处理本次request请求的执行链对象,它是连接url请求和执行链对象的桥梁。执行链又是什么东东呢?无论是spring mvc还是struts2,执行链往往就是若干个拦截器加一个处理请求的方法对象(又被称为处理器)。

       spring在启动过程中会自动注册两个处理器映射对象,分别是“RequestMappingHandlerMapping”和“BeanNameUrlHandlerMapping”,前者的优先级最高,优先使用RequestMappingHandlerMapping来处理请求,只有在其无法获取请求的处理器情况下才会使用其它映射器。所以今天我主要就是分析下RequestMappingHandlerMapping对象。

       照例,还是先上一张类结构图,然后我从初始化和处理请求两个方面来讲解。

SpringMVC 映射器和注解驱动 springmvc映射处理器_Spring


一. 拦截器分类和注册处理方法对象

        从上图可以看出处理器映射实现了ApplicationContextAware接口,在创建处理器映射过程中的初始化bean阶段,spring系统自动注册的bean后处理器

ApplicationContextAwareProcessor会调用setApplicationContext(ac) 方法,为处理器映射对象赋值容器对象。就是在这个方法中处理器映射获取了容器之后执行拦截器分类和注册处理方法这些操作。 具体实现上,通过重写的方式,AbstractHandlerMapping, AbstractHandlerMethodMapping, WebApplicationObjectSupport 这三个类共同实现了setApplicationContext(ac) 方法。

1. AbstractHandlerMapping负责拦截器分类,它会把拦截器分成两类,一类对所有请求都适用,一类只对特定路径的请求适用,分别置入以下两个成员变量。

private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();

private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();

那么,拦截器都是从哪里来的呢?使用<mvc:interceptor>标签配置的拦截器,spring启动时会被解析成MappedInterceptor类型拦截器,然后被置入bean工厂,AbstractHandlerMapping就是从bean工厂获取到这些拦截器;还有一种途径是,用户配置注入到AbstractHandlerMapping的成员变量interceptors,这个变量的类型是List<Object>,其支持的拦截器类型有“HandlerInterceptor”,“WebRequestInterceptor”,“MappedInterceptor”。

2. AbstractHandlerMethodMapping负责注册所有处理器的处理方法,其过程如下:

       1. 默认从子web容器中获取所有bean。

       2. 找出其中的处理器bean,即bean是否被@Controller注解了。

发现处理方法后把它的@RequestMapping注解解析成RequestMappingInfo对象,再把方法对象包装成HandlerMethod对象。

把RequestMappingInfo和HandlerMethod对象对象置入缓存handlerMethods。

      5. 从RequestMappingInfo中获取非模式的请求路径集合,把非模式请求路径和RequestMappingInfo置入缓存urlMap。


3. WebApplicationObjectSupport负责从web容器获取servletContext赋值给成员变量。



二. 获取执行链对象

       获取执行链对象是HandlerMapping接口定义的唯一方法,执行链其实就是若干个拦截器和一个处理器构成的。我们可以看下该类的源码定义,

public class HandlerExecutionChain 
{
	/**
	 * 处理器
	 */
	private final Object handler;
	/**
	 * 拦截器列表
	 */
	private List<HandlerInterceptor> interceptorList;

	....
}


拦截器是如何获取的呢?

       上文说了,启动过程中AbstractHandlerMapping会把bean工厂中的拦截器分成两类,一类适用于所有请求,一类适用于特定路径的请求。那么,适用于所有请求的拦截器肯定是要返回的;另一类适用特定路径的拦截器的类型是MappedInterceptor,它持有适用该拦截器的模式请求路径数组,如果当前请求的路径跟其中某个元素匹配,则返回该拦截器。

处理器如何获取呢?

1. 把请求路径作为key,尝试直接从缓存urlMap中获取RequestMappingInfo对象,urlMap中缓存的是非模式请求路径和RequestMappingInfo的对应关系。也就是说先把请求路径当成非模式路径来处理,如果能够获取到RequestMappingInfo对,则把RequestMappingInfo做为key,从handlerMethods中获取处理器。

2. 如果步骤1未成功,则遍历handlerMethods的所有key,handlerMethods的key是RequestMappingInfo对象,从中找出所有能跟当前请求匹配的RequestMappingInfo对象。如果最终有多个RequestMappingInfo对象匹配当前请求,则选出匹配度最高的RequestMappingInfo对象,最终根据RequestMappingInfo从缓存handlerMethods中获取HandlerMethod对象。

3. 获取了HandlerMethod对象后,还会执行几个比较重要的操作,如,

    a. 把请求路径置入request对象的属性集,key是“HandlerMapping.pathWithinHandlerMapping”。

    b. 从请求路径中提取模板变量,置入request对象的属性集,key是“HandlerMapping.uriTemplateVariables”。

4. 如果步骤1和步骤2都不成功,则返回null。