SpringMVC中的HandlerMapping

DispatcherServlet中持有一个 List<HandlerMapping> 的组件,那么HandlerMapping究竟是什么东西呢?

我们通过debug的方式看看 handlerMappings 里面具体有什么?

可以看到这 handlerMappings 这个list里面一共有5个元素,这5个中有3个是WebMvcConfigurationSupportXXX类型的代理对象,另外2个是RequestMappingHandlerMappingBeanNameUrlHandlerMapping

我们看看官方文档上又是怎么说的,在https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-servlet 这个页面里有对HandlerMapping的说明。

从官方描述看,HandlerMapping是将请求与处理该请求的控制器(controller)、拦截器(intercepter)做了一个映射关系。对于一个请求,通过特定的HandlerMapping,就能找到需要用哪个controller的哪个方法来处理请求,并且处理这个请求需要执行哪些拦截器。这个寻找功能就是HandlerMapping接口中声明的HandlerExecutionChain getHandler(HttpServletRequest request)方法,该方法返回了一个叫 HandlerExecutionChain(处理器执行链)的实例,其中包装了控制器、拦截器列表这些数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface HandlerMapping {

//......

/**
* Return a handler and any interceptors for this request. The choice may be made
* on request URL, session state, or any factor the implementing class chooses.
* <p>The returned HandlerExecutionChain contains a handler Object, rather than
* even a tag interface, so that handlers are not constrained in any way.
* For example, a HandlerAdapter could be written to allow another framework's
* handler objects to be used.
* <p>Returns {@code null} if no match was found. This is not an error.
* The DispatcherServlet will query all registered HandlerMapping beans to find
* a match, and only decide there is an error if none can find a handler.
* @param request current HTTP request
* @return a HandlerExecutionChain instance containing handler object and
* any interceptors, or {@code null} if no mapping found
* @throws Exception if there is an internal error
*/
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

通过debug的方式查看HandlerExecutionChain对象中封装的信息。

查看HandlerExecutionChain的源码可以看到它持有一个Object类型成员变量 handler ,它可能是一个HandlerMethod(封装了xxxController及请求对应的方法等)对象,也有可能是一个Controller对象、HttpRequestHandler 对象或Servlet对象,而这个 handler 具体是什么对象,是与所使用的 HandlerMapping 实现类有关。

前面也说到 DispatcherServlet 中的 handlerMappings 中有好几个不同的HandlerMapping实例,并且对于一个请求,会由其中的一个实例来获取到HandlerExecutionChain对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}

那么,一个具体请求到底会通过哪个HandlerMapping实例来获取HandlerExecutionChain对象呢?这就又要回到官方文档里描述的,HandlerMapping接口的两个主要实现类是RequestMappingHandlerMappingSimpleUrlHandlerMapping,它们两个分别用来处理SpringMVC中不同形式的url映射方式。

我们知道可以通过注解@Controller@RestController注解方式来声明一个 Controller,然后使用@RequestMapping来修饰类或方法,这种方式其实是由RequestMappingHandlerMapping映射处理器来对请求进行映射的。也可以概括地说,RequestMappingHandlerMapping是专门支持@RequestMapping注解声明方式的请求映射处理器。

那么SimpleUrlHandlerMapping又用于处理哪种情况呢,那就是采用实现Controller接口或实现HttpRequestHandler接口的类,这种声明Controller的方式是早期的做法,现在在实际开发中用的很少了但是也是SpringMVC所支持的。

如下我们分别新建两个类分别实现Controller接口和实现HttpRequestHandler接口做具体的演示:

  1. 实现Controller接口的方式:

  2. 实现HttpRequestHandler接口的方式:

除了我们自己的实现,SpringMVC框架本身也有类实现了Controller接口等,比如:UrlFilenameViewController类、ServletForwardingController类等,但这些类的实例是需要我们通过配置去注入到IOC容器中。

有了上面的实现类,再进行debug,就可以验证前面对于RequestMappingHandlerMappingSimpleUrlHandlerMapping的论述了,至此我们应该也就大概了解SpringMVC中的 HandlerMapping 是什么了。

------ 本文完 ------