SpringMVC源码之Controller查找原理 (2)

这里T的类型是RequestMappingInfo。这个对象就是封装的具体Controller下的方法的RequestMapping注解的相关信息。一个RequestMapping注解对应一个RequestMappingInfo对象。HandlerMethod和RequestMappingInfo类似,是对Controlelr下具体处理方法的封装。先看方法的第一行,根据handler和mehthod创建HandlerMethod对象。第二行通过handlerMethods map来获取当前mapping对应的HandlerMethod。然后判断是否存在相同的RequestMapping配置。如下这种配置就会导致此处抛
Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map...
异常

@Controller @RequestMapping("/AmbiguousTest") public class AmbiguousTestController { @RequestMapping(value = "/test1") @ResponseBody public String test1(){ return "method test1"; } @RequestMapping(value = "/test1") @ResponseBody public String test2(){ return "method test2"; } }

在SpingMVC启动(初始化)阶段检查RequestMapping配置是否有歧义,这是其中一处检查歧义的(后面还会提到一个在运行时检查歧义性的地方)。然后确认配置正常以后会把该RequestMappingInfo和HandlerMethod对象添加至handlerMethods(LinkedHashMap

registerHandlerMethod方法简单总结

该方法的主要有3个职责

检查RequestMapping注解配置是否有歧义。

构建RequestMappingInfo到HandlerMethod的映射map。该map便是AbstractHandlerMethodMapping的成员变量handlerMethods。LinkedHashMap

构建AbstractHandlerMethodMapping的成员变量urlMap,MultiValueMap

@Controller @RequestMapping("/UrlMap") public class UrlMapController { @RequestMapping(value = "/test1", method = RequestMethod.GET) @ResponseBody public String test1(){ return "method test1"; } @RequestMapping(value = "/test1") @ResponseBody public String test2(){ return "method test2"; } @RequestMapping(value = "/test3") @ResponseBody public String test3(){ return "method test3"; } }

初始化完成后,对应AbstractHandlerMethodMapping的urlMap的结构如下

SpringMVC源码之Controller查找原理

以上便是SpringMVC初始化的主要过程

查找过程

为了理解查找流程,带着一个问题来看,现有如下Controller

@Controller @RequestMapping("/LookupTest") public class LookupTestController { @RequestMapping(value = "/test1", method = RequestMethod.GET) @ResponseBody public String test1(){ return "method test1"; } @RequestMapping(value = "/test1", headers = "Referer=https://www.baidu.com") @ResponseBody public String test2(){ return "method test2"; } @RequestMapping(value = "/test1", params = "id=1") @ResponseBody public String test3(){ return "method test3"; } @RequestMapping(value = "/*") @ResponseBody public String test4(){ return "method test4"; } }

有如下请求

SpringMVC源码之Controller查找原理

这个请求会进入哪一个方法?

web容器(Tomcat、jetty)接收请求后,交给DispatcherServlet处理。FrameworkServlet调用对应请求方法(eg:get调用doGet),然后调用processRequest方法。进入processRequest方法后,一系列处理后,在line:936进入doService方法。然后在Line856进入doDispatch方法。在line:896获取当前请求的处理器handler。然后进入AbstractHandlerMethodMapping的lookupHandlerMethod方法。代码如下

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); //根据uri获取直接匹配的RequestMappingInfos List<T> directPathMatches = this.urlMap.get(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } //不存在直接匹配的RequetMappingInfo,遍历所有RequestMappingInfo if (matches.isEmpty()) { // No choice but to go through all mappings addMatchingMappings(this.handlerMethods.keySet(), matches, request); } //获取最佳匹配的RequestMappingInfo对应的HandlerMethod if (!matches.isEmpty()) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); Collections.sort(matches, comparator); if (logger.isTraceEnabled()) { logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches); } //再一次检查配置的歧义性 Match bestMatch = matches.get(0); if (matches.size() > 1) { Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); throw new IllegalStateException( "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" + m1 + ", " + m2 + "}"); } } handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(handlerMethods.keySet(), lookupPath, request); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxfwg.html