DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

DispatcherServlet如何找到正确的Controller(二)

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

PatternsRequestCondition:

图片[1]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

图片[2]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

RequestMethodsRequestCondition:

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

图片[3]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

其他相关的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的获得(直接看关键代码了):

图片[4]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

这里的比较器是使用RequestMappingInfo的compareTo方法(RequestCondition接口定义的)。

图片[5]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

然后构造HandlerExecutionChain加上拦截器

图片[6]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

实例

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

@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还是有的。

图片[7]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

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

会先根据 “/wildcard/test1” 找urlMap对应的RequestMappingInfo集合,找不到的话取handlerMethods集合中所有的key集合(也就是RequestMappingInfo集合)。

然后进行匹配,匹配根据RequestCondition的getMatchingCondition方法。

图片[8]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

最终匹配到2个RequestMappingInfo:

图片[9]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

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

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

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

我们看下PatternsRequestCondition比较的逻辑:

图片[10]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

图片[11]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

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

图片[12]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

直接比较优先于通配符。

图片[13]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

@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不一样。

图片[14]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

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

图片[15]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

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

资源文件映射

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

SpringMVC中还有静态文件的映射,SimpleUrlHandlerMapping。

DispatcherServlet找对应的HandlerExecutionChain的时候会遍历属性handlerMappings,这个一个实现了HandlerMapping接口的集合。

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

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

Spring解析配置文件会使用ResourcesBeanDefinitionParser进行解析的时候,会实例化出SimpleUrlHandlerMapping。

图片[16]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

图片[17]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

其中注册的HandlerMethod为ResourceHttpRequestHandler。

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

图片[18]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

图片[19]-DispatcherServlet如何找到正确的Controller(二)-Spring专区论坛-技术-SpringForAll社区

地址匹配到/static/**。

最终SimpleUrlHandlerMapping找到对应的Handler -> ResourceHttpRequestHandler。

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

总结

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

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

请登录后发表评论

    没有回复内容