1.1 前言

  • 上一章节介绍了Spring Mvc执行一个普通请求的整体流程,这一章节介绍其请求映射处理组件HandlerMapping 的作用

  • HandlerMapping负责根据request请求找到对应的Handler处理器及Interceptor拦截器,并将它们封装在HandlerExecutionChain对象内,返回给中央调度器

2.1 请求映射处理组件HandlerMapping 解析

2.1.1 HandlerMapping 解析

  • 先来回顾核心方法体doDispatch(HttpServletRequest request, HttpServletResponse response),查看HandlerMapping 对应的处理逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// 获取当前请求的WebAsyncManager,如果没找到则创建并与请求关联
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {
ModelAndView mv = null;
Exception dispatchException = null;

try {
// 检查是否有 Multipart,有则将请求转换为 Multipart 请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// Determine handler for the current request.
// 遍历所有的 HandlerMapping 找到与请求对应的 Handler,并将其与一堆拦截器封装到 HandlerExecution 对象中
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
// 遍历所有的 HandlerAdapter,找到可以处理该 Handler 的 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
// 处理 last-modified 请求头
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.
// 执行实际的处理程序
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

applyDefaultViewName(processedRequest, mv);
// 遍历拦截器,执行它们的 postHandle() 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 处理执行结果,是一个 ModelAndView 或 Exception,然后进行渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
// 遍历拦截器,执行它们的 afterCompletion() 方法
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
  • 下面是请求映射处理组件HandlerMapping 主要的处理代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    HandlerExecutionChain mappedHandler = null;
    ...

    // Determine handler for the current request.
    // 遍历所有的 HandlerMapping 找到与请求对应的 Handler,并将其与一堆拦截器封装到 HandlerExecution 对象中
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null || mappedHandler.getHandler() == null) {
    noHandlerFound(processedRequest, response);
    return;
    }
  • 继续在doDispatch方法中mappedHandler = getHandler(processedRequest); 这行代码打好断点,进入该方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   /** List of HandlerMappings used by this servlet */
private List<HandlerMapping> handlerMappings;

/**
* 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
*/
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 循环handlerMappings eg: RequestMappingHandlerMapping、SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
// 遍历HandlerMapping实现类的找到对应的 HandlerExecutionChain
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
// 注意这里是找到第一个就直接返回
return handler;
}
}
return null;
}
  • 可以看到是遍历了this.handlerMappings这个对象,这个对象是个list, 在调试过程中,我们可以看到,默认加载的是如下图所示的的HandlerMapping
  • this.handlerMappings这个对象是怎么初始化的呢

    • 回顾第二章节的DispatcherServletonRefresh(wac);方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      protected void onRefresh(ApplicationContext context) {
      // initStrategies方法内部会初始化各个策略接口的实现类。
      initStrategies(context);
      }
      protected void initStrategies(ApplicationContext context) {
      // 上传组件组件初始化
      initMultipartResolver(context);
      initLocaleResolver(context);
      initThemeResolver(context);
      // 请求映射处理组件初始化
      initHandlerMappings(context);
      // 处理适配器组建初始化
      initHandlerAdapters(context);
      // 异常处理组件初始化
      initHandlerExceptionResolvers(context);
      initRequestToViewNameTranslator(context);
      // 视图处理组件初始化
      initViewResolvers(context);
      initFlashMapManager(context);
      }
    • 进入initHandlerMappings(context); 方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      private void initHandlerMappings(ApplicationContext context) {
      this.handlerMappings = null;

      if (this.detectAllHandlerMappings) {
      // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
      // 在ApplicationContext bean中找到所有HandlerMappings, beansOfTypeIncludingAncestors 返回给定类型或子类型的所有bean
      Map<String, HandlerMapping> matchingBeans =
      BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
      if (!matchingBeans.isEmpty()) {
      this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
      // We keep HandlerMappings in sorted order.
      AnnotationAwareOrderComparator.sort(this.handlerMappings);
      }
      }
      else {
      try {
      HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
      this.handlerMappings = Collections.singletonList(hm);
      }
      catch (NoSuchBeanDefinitionException ex) {
      // Ignore, we'll add a default HandlerMapping later.
      }
      }

      // Ensure we have at least one HandlerMapping, by registering
      // a default HandlerMapping if no other mappings are found.
      if (this.handlerMappings == null) {
      // 在这里将设置
      this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
      if (logger.isDebugEnabled()) {
      logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
      }
      }
      }
    • 关注this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); 这行代码,可以看到this.handlerMappings 在这里赋值,继续进入该方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
      String key = strategyInterface.getName();
      String value = defaultStrategies.getProperty(key);
      if (value != null) {
      String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
      List<T> strategies = new ArrayList<T>(classNames.length);
      for (String className : classNames) {
      try {
      Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
      Object strategy = createDefaultStrategy(context, clazz);
      strategies.add((T) strategy);
      }
      catch (ClassNotFoundException ex) {
      throw new BeanInitializationException(
      "Could not find DispatcherServlet's default strategy class [" + className +
      "] for interface [" + key + "]", ex);
      }
      catch (LinkageError err) {
      throw new BeanInitializationException(
      "Error loading DispatcherServlet's default strategy class [" + className +
      "] for interface [" + key + "]: problem with class file or dependent class", err);
      }
      }
      return strategies;
      }
      else {
      return new LinkedList<T>();
      }
      }
    • 从可以看到String value = defaultStrategies.getProperty(key); 这里使用了我们的配置对象private static final Properties defaultStrategies;

      • defaultStrategies 是哪里初始化的呢?可以看到是在static代码块中读取一个配置文件并把它注册为Properties defaultStrategies对象

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        /**
        * Name of the class path resource (relative to the DispatcherServlet class)
        * that defines DispatcherServlet's default strategy names.
        */
        private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";

        ...

        static {
        // Load default strategy implementations from properties file.
        // This is currently strictly internal and not meant to be customized
        // by application developers.
        try {
        // DEFAULT_STRATEGIES_PATH: DispatcherServlet.properties
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
        throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
        }
        }
      • 这里是读取了DEFAULT_STRATEGIES_PATH变量,查看变量可以发现是DispatcherServlet.properties,所以查看DispatcherServlet.properties文件,该文件在Spring web mvc 包下

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        # Default implementation classes for DispatcherServlet's strategy interfaces.
        # Used as fallback when no matching beans are found in the DispatcherServlet context.
        # Not meant to be customized by application developers.

        org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

        org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

        org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
        org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

        org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
        org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
        org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

        org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
        org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
        org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

        org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

        org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

        org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
    • 上面配置的了org.springframework.web.servlet.HandlerMapping默认有BeanNameUrlHandlerMappingDefaultAnnotationHandlerMapping,所以之后执行createDefaultStrategy 方法就是根据Class对象来创建bean

      1
      2
      3
      protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
      return context.getAutowireCapableBeanFactory().createBean(clazz);
      }
  • 下面来介绍三种常用的HandlerMapping的作用

    • RequestMappingHandlerMapping是三个中最常用的handlerMapping,使用注解方式最为方便快捷,SpringMvc项目开发都是采用这种形式,配合@RequestMapping()相关注释就可以完成开发

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      <!-- 注册HandlerMapper、HandlerAdapter两个映射类 -->
      <mvc:annotation-driven />

      <!-- 访问静态资源 -->
      <mvc:default-servlet-handler />

      <!-- 配置扫描的包 -->
      <context:component-scan base-package="com.songsy.*" />

      @RestController
      @RequestMapping("/hello")
      public class HelloController {
      protected final Log logger = LogFactory.getLog(this.getClass());

      @RequestMapping("/index")
      public String index(){
      return "test";
      }
      }
    • SimpleUrlHandlerMapping

      SimpleUrlHandlerMappingController处理类需要实现Controller接口,并注册成Bean就可以完成配置,处理逻辑写在handleRequest方法体内

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
      <property name="mappings">
      <props>
      <prop key="/simpleUrlHandlerMapping.do">welcomeController</prop>
      </props>
      </property>
      </bean>

      /**
      * @author Rob Harrop
      */
      public class WelcomeController implements Controller {

      @Override
      public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
      return new ModelAndView("welcomeView");
      }

      }
    • BeanNameUrlHandlerMapping需要配置 <bean id="/index" class="com.alipay.web.TestController" />,注意在beanid中要加上斜杆,Controller方面的测试代码跟前面的SimpleUrlHandlerMapping一样,实现Controller,重写handlerRequest()方法即可。

  • 下一章节来跟进RequestMappingHandlerMapping这个HandlerMapping 的具体实现

3.1 总结

  • HandlerMapping我们知道他的作用是根据request找到对应的HandlerHandler具体表现形式可以为类,也可以为方法,上面的三种常用的HandlerMapping有其介绍,我们平常使用@RequestMapping注解来标识一个方法,这个注解的作用就是将这个方法注册为Handler
  • 为什么需要要多种HandlerMapping呢,当然是为了其可扩展性,实现HandlerMapping接口就可以实现自定义Handler的获取,从而实现定制化
  • Spring Mvc 大量使用了模版方法模式,父类定义流程,子类实现,而这些口子都是所谓的模板方法,可以自由指定,从而保证了灵活性,良好的抽象设计,是整个框架变得非常灵活
  • Spring Mvc 核心类中所有的变量声明,几乎都以接口的形式给出,并没有绑定在具体的实现类上

4.1 参考

官方文档: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html

https://www.jianshu.com/p/e4f1c9326223

https://blog.csdn.net/gaoshan12345678910/article/details/81778587

https://blog.csdn.net/lang_programmer/article/details/71598042