欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

程序员文章站 2022-06-20 23:10:25
SpringMVC是目前主流的Web MVC框架之一。 我们使用浏览器通过地址 http://ip:port/contextPath/path进行访问,SpringMVC是如何得知用户到底是访问哪个Controller中的方法,这期间到底发生了什么。 本文将分析SpringMVC是如何处理请求与Co ......

springmvc是目前主流的web mvc框架之一。 

我们使用浏览器通过地址 http://ip:port/contextpath/path进行访问,springmvc是如何得知用户到底是访问哪个controller中的方法,这期间到底发生了什么。

本文将分析springmvc是如何处理请求与controller之间的映射关系的,让读者知道这个过程中到底发生了什么事情。

本文实际上是在上文基础上,深入分析

handlermapping里的
handlerexecutionchain gethandler(httpservletrequest var1) throws exception;
该方法的具体实现,包括它如何找到对应的方法,以及如何把结果保存在map里,以便让请求转发到对应的handler上,同时也分析了handleradaptor具体做了什么事情。

源码分析

在分析源码之前,我们先了解一下几个东西。

1.这个过程中重要的接口和类。

handlermethod类:

  spring3.1版本之后引入的。 是一个封装了方法参数、方法注解,方法返回值等众多元素的类。

  SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

  它的子类invocablehandlermethod有两个重要的属性webdatabinderfactory和handlermethodargumentresolvercomposite, 很明显是对请求进行处理的。

  invocablehandlermethod的子类servletinvocablehandlermethod有个重要的属性handlermethodreturnvaluehandlercomposite,很明显是对响应进行处理的。

  servletinvocablehandlermethod这个类在handleradapter对每个请求处理过程中,都会实例化一个出来(上面提到的属性由handleradapter进行设置),分别对请求和返回进行处理。  (requestmappinghandleradapter源码,实例化servletinvocablehandlermethod的时候分别set了上面提到的重要属性)

  SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

methodparameter类:

  handlermethod类中的parameters属性类型,是一个methodparameter数组。methodparameter是一个封装了方法参数具体信息的工具类,包括参数的的索引位置,类型,注解,参数名等信息。

  handlermethod在实例化的时候,构造函数中会初始化这个数组,这时只初始化了部分数据,在handleradapter对请求处理过程中会完善其他属性,之后交予合适的handlermethodargumentresolver接口处理。

  以类deptcontroller为例:

@controller
@requestmapping(value = "/dept")
public class deptcontroller {

  @autowired
  private ideptservice deptservice;

  @requestmapping("/update")
  @responsebody
  public string update(dept dept) {
    deptservice.saveorupdate(dept);
    return "success";
  }

}

  (刚初始化时的数据)  

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

  (handleradapter处理后的数据)

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

requestcondition接口:

  spring3.1版本之后引入的。 是springmvc的映射基础中的请求条件,可以进行combine, compareto,getmatchingcondition操作。这个接口是映射匹配的关键接口,其中getmatchingcondition方法关乎是否能找到合适的映射。

  SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

requestmappinginfo类:

  spring3.1版本之后引入的。 是一个封装了各种请求映射条件并实现了requestcondition接口的类。

  有各种requestcondition实现类属性,patternscondition,methodscondition,paramscondition,headerscondition,consumescondition以及producescondition,这个请求条件看属性名也了解,分别代表http请求的路径模式、方法、参数、头部等信息。

  SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

requestmappinghandlermapping类:

   处理请求与handlermethod映射关系的一个类。

2.web服务器启动的时候,springmvc到底做了什么。

先看abstracthandlermethodmapping的inithandlermethods方法中。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

我们进入createrequestmappinginfo方法看下是如何构造requestmappinginfo对象的。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

patternsrequestcondition构造函数:

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

类对应的requestmappinginfo存在的话,跟方法对应的requestmappinginfo进行combine操作。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

然后使用符合条件的method来注册各种handlermethod。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

下面我们来看下各种requestcondition接口的实现类的combine操作。

patternsrequestcondition:

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

requestmethodsrequestcondition:

方法的请求条件,用个set直接add即可。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

其他相关的requestconditon实现类读者可自行查看源码。

最终,requestmappinghandlermapping中两个比较重要的属性

private final map<t, handlermethod> handlermethods = new linkedhashmap<t, handlermethod>();

private final multivaluemap<string, t> urlmap = new linkedmultivaluemap<string, t>();

t为requestmappinginfo。

构造完成。

我们知道,springmvc的分发器dispatcherservlet会根据浏览器的请求地址获得handlerexecutionchain。

这个过程我们看是如何实现的。

首先看handlermethod的获得(直接看关键代码了):

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

这里的比较器是使用requestmappinginfo的compareto方法(requestcondition接口定义的)。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

然后构造handlerexecutionchain加上拦截器

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

实例

写了这么多,来点例子让我们验证一下吧。

@controller
@requestmapping(value = "/wildcard")
public class testwildcardcontroller {

  @requestmapping("/test/**")
  @responsebody
  public string test1(modelandview view) {
    view.setviewname("/test/test");
    view.addobject("attr", "testwildcardcontroller -> /test/**");
    return view;
  }

  @requestmapping("/test/*")
  @responsebody
  public string test2(modelandview view) {
    view.setviewname("/test/test");
    view.addobject("attr", "testwildcardcontroller -> /test*");
    return view;
  }

  @requestmapping("test?")
  @responsebody
  public string test3(modelandview view) {
    view.setviewname("/test/test");
    view.addobject("attr", "testwildcardcontroller -> test?");
    return view;
  }

  @requestmapping("test/*")
  @responsebody
  public string test4(modelandview view) {
    view.setviewname("/test/test");
    view.addobject("attr", "testwildcardcontroller -> test/*");
    return view;
  }

}

由于这里的每个pattern都带了*因此,都不会加入到urlmap中,但是handlermethods还是有的。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

当我们访问:http://localhost:8888/springmvcdemo/wildcard/test1的时候。

会先根据 "/wildcard/test1" 找urlmap对应的requestmappinginfo集合,找不到的话取handlermethods集合中所有的key集合(也就是requestmappinginfo集合)。

然后进行匹配,匹配根据requestcondition的getmatchingcondition方法。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

最终匹配到2个requestmappinginfo:

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

然后会使用比较器进行排序。

之前也分析过,比较器是有优先级的。

我们看到,requestmappinginfo除了pattern,其他属性都是一样的。

我们看下patternsrequestcondition比较的逻辑:

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

因此,/test*的通配符比/test?的多,因此,最终选择了/test?

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

直接比较优先于通配符。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

@controller
@requestmapping(value = "/priority")
public class testprioritycontroller {

  @requestmapping(method = requestmethod.get)
  @responsebody
  public string test1(modelandview view) {
    view.setviewname("/test/test");
    view.addobject("attr", "其他condition相同,带有method属性的优先级高");
    return view;
  }

  @requestmapping()
  @responsebody
  public string test2(modelandview view) {
    view.setviewname("/test/test");
    view.addobject("attr", "其他condition相同,不带method属性的优先级高");
    return view;
  }

}

 这里例子,其他requestcondition都一样,只有requestmethodcondition不一样。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

看出,方法多的优先级越多。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

至于其他的requestcondition,大家自行查看源码吧。

资源文件映射

以上分析均是基于controller方法的映射(requestmappinghandlermapping)。

springmvc中还有静态文件的映射,simpleurlhandlermapping。

dispatcherservlet找对应的handlerexecutionchain的时候会遍历属性handlermappings,这个一个实现了handlermapping接口的集合。

由于我们在*-dispatcher.xml中加入了以下配置:

<mvc:resources location="/static/" mapping="/static/**"/>

 spring解析配置文件会使用resourcesbeandefinitionparser进行解析的时候,会实例化出simpleurlhandlermapping。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

其中注册的handlermethod为resourcehttprequesthandler。

访问地址:http://localhost:8888/springmvcdemo/static/js/jquery-1.11.0.js

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

地址匹配到/static/**。

最终simpleurlhandlermapping找到对应的handler -> resourcehttprequesthandler。

resourcehttprequesthandler进行handlerequest的时候,直接输出资源文件的文本内容。

总结

大致上整理了一下springmvc对请求的处理,包括其中比较关键的类和接口,希望对读者有帮助。

让自己对springmvc有了更深入的认识,也为之后分析数据绑定,拦截器、handleradapter等打下基础。