SpringMVC源码之Controller查找原理

2018-02-06  本文已影响0人  卧颜沉默

摘要

SpringMVC请求流程

image.png

SpringMVC初始化过程

理解初始化过程之前,先认识两个类

  1. RequestMappingInfo类,对RequestMapping注解封装。里面包含http请求头的相关信息。如uri、method、params、header等参数。一个对象对应一个RequestMapping注解
  2. HandlerMethod类,是对Controller的处理请求方法的封装。里面包含了该方法所属的bean对象、该方法对应的method对象、该方法的参数等。


    image.png
protected void initHandlerMethods() {
  if (logger.isDebugEnabled()) {
      logger.debug("Looking for request mappings in application context: " + getApplicationContext());
  }
  //获取applicationContext中所有的bean name
  String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
        BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
        getApplicationContext().getBeanNamesForType(Object.class));
  //遍历beanName数组
  for (String beanName : beanNames) {
      //isHandler会根据bean来判断bean定义中是否带有Controller注解或RequestMapping注解
      if (isHandler(getApplicationContext().getType(beanName))){
        detectHandlerMethods(beanName);
      }
  }
  handlerMethodsInitialized(getHandlerMethods());
}
@Override
protected boolean isHandler(Class<?> beanType) {
  return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
        (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}
protected void detectHandlerMethods(final Object handler) {
  //获取到当前Controller bean的class对象
  Class<?> handlerType = (handler instanceof String) ?
        getApplicationContext().getType((String) handler) : handler.getClass();
  //同上,也是该Controller bean的class对象
  final Class<?> userType = ClassUtils.getUserClass(handlerType);
  //获取当前bean的所有handler method。这里查找的依据便是根据method定义是否带有RequestMapping注解。如果有根据注解创建RequestMappingInfo对象
  Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
      public boolean matches(Method method) {
        return getMappingForMethod(method, userType) != null;
      }
  });
  //遍历并注册当前bean的所有handler method
  for (Method method : methods) {
      T mapping = getMappingForMethod(method, userType);
      //注册handler method,进入以下方法
      registerHandlerMethod(handler, method, mapping);
  }
}
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
  RequestMappingInfo info = null;
   //获取method的@RequestMapping注解
  RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
  if (methodAnnotation != null) {
      RequestCondition<?> methodCondition = getCustomMethodCondition(method);
      info = createRequestMappingInfo(methodAnnotation, methodCondition);
       //获取method所属bean的@RequtestMapping注解
      RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
      if (typeAnnotation != null) {
        RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
        //合并两个@RequestMapping注解
        info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
      }
  }
  return info;
}
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
  //创建HandlerMethod
  HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
  HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
  //检查配置是否存在歧义性
  if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
      throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean()
            + "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '"
            + oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
  }
  this.handlerMethods.put(mapping, newHandlerMethod);
  if (logger.isInfoEnabled()) {
      logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
  }
  //获取@RequestMapping注解的value,然后添加value->RequestMappingInfo映射记录至urlMap中
  Set<String> patterns = getMappingPathPatterns(mapping);
  for (String pattern : patterns) {
      if (!getPathMatcher().isPattern(pattern)) {
        this.urlMap.add(pattern, mapping);
      }
  }
}
@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";
    }
}
registerHandlerMethod方法简单总结

该方法的主要有3个职责

  1. 检查RequestMapping注解配置是否有歧义。
  2. 构建RequestMappingInfo到HandlerMethod的映射map。该map便是AbstractHandlerMethodMapping的成员变量handlerMethods。LinkedHashMap<RequestMappingInfo,HandlerMethod>。
  3. 构建AbstractHandlerMethodMapping的成员变量urlMap,MultiValueMap<String,RequestMappingInfo>。这个数据结构可以把它理解成Map<String,List<RequestMappingInfo>>。其中String类型的key存放的是处理方法上RequestMapping注解的value。就是具体的uri
    先有如下Controller
@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";
    }
}

查找过程

@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";
    }
}
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);
  }
}
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
  for (T mapping : mappings) {
      T match = getMatchingMapping(mapping, request);
      if (match != null) {
        matches.add(new Match(match, handlerMethods.get(mapping)));
      }
  }
}
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
  int result = patternsCondition.compareTo(other.getPatternsCondition(), request);
  if (result != 0) {
      return result;
  }
  result = paramsCondition.compareTo(other.getParamsCondition(), request);
  if (result != 0) {
      return result;
  }
  result = headersCondition.compareTo(other.getHeadersCondition(), request);
  if (result != 0) {
      return result;
  }
  result = consumesCondition.compareTo(other.getConsumesCondition(), request);
  if (result != 0) {
      return result;
  }
  result = producesCondition.compareTo(other.getProducesCondition(), request);
  if (result != 0) {
      return result;
  }
  result = methodsCondition.compareTo(other.getMethodsCondition(), request);
  if (result != 0) {
      return result;
  }
  result = customConditionHolder.compareTo(other.customConditionHolder, request);
  if (result != 0) {
      return result;
  }
  return 0;
}
@RequestMapping(value = "/test5", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String test5(){
    return "method test5";
}
@RequestMapping(value = "/test5", method = {RequestMethod.GET, RequestMethod.DELETE})
@ResponseBody
public String test6(){
    return "method test6";
}
public int compareTo(RequestMethodsRequestCondition other, HttpServletRequest request) {
  return other.methods.size() - this.methods.size();
}

总结

版权声明
作者:wycm
出处:https://www.jianshu.com/p/08338ea28647

您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

欢迎关注我的个人公众号
上一篇 下一篇

猜你喜欢

热点阅读