Spring-mvc-MappingRegistry

举报
龙哥手记 发表于 2022/11/16 23:10:59 2022/11/16
【摘要】 《读尽源码 第十四篇》

MappingRegistry

  • 源码阅读仓库: SourceHot-spring

  • 源码路径: org.springframework.jms.annotation.EnableJms

  • 类全路径

  • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry

  • 基本属性

    class MappingRegistry {
    
       /**
        * key:mapping
        * value: mapping registration
        */
       private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
    
       /**
        * key: mapping
        * value: handlerMethod
        */
       private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
    
       /**
        * key: url
        * value: list mapping
        */
       private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
    
       /**
        * key: name
        * value: handler method
        */
       private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
    
       /**
        * key:handler method
        * value: 跨域配置
        */
       private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
    
       /**
        * 读写锁
        */
       private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    }Copy to clipboardErrorCopied
  • 写一个简单的 controller 来进行解析

@RestController
@RequestMapping("/demo")
public class DemoController {
   @GetMapping("/do")
   public Object go() {
      return "fff";
   }
}Copy to clipboardErrorCopied
  • 前置链路追踪

    • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#registerHandlerMethod
    protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
        super.registerHandlerMethod(handler, method, mapping);
        this.updateConsumesCondition(mapping, method);
    }Copy to clipboardErrorCopied
    • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod
    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        this.mappingRegistry.register(mapping, handler, method);
    }Copy to clipboardErrorCopied
    • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register

      本文重点的方法

createHandlerMethod

  • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#createHandlerMethod
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
   // 是否是字符串
   if (handler instanceof String) {
      // 创建对象
      return new HandlerMethod((String) handler,
            obtainApplicationContext().getAutowireCapableBeanFactory(), method);
   }
   return new HandlerMethod(handler, method);
}Copy to clipboardErrorCopied
  • HandlerMethod 构造函数

    public HandlerMethod(String beanName, BeanFactory beanFactory, Method method){}
    
    public HandlerMethod(Object bean, Method method) {}Copy to clipboardErrorCopied

HandlerMethod

  • 成员变量
public class HandlerMethod {

   /** Logger that is available to subclasses. */
   protected final Log logger = LogFactory.getLog(getClass());

   /**
    * beanName 或者 bean 实例
    */
   private final Object bean;

   /**
    * 上下文
    */
   @Nullable
   private final BeanFactory beanFactory;

   /**
    * bean 类型
    */
   private final Class<?> beanType;

   /**
    * 处理方法
    */
   private final Method method;

   private final Method bridgedMethod;

   /**
    * 方法参数
    */
   private final MethodParameter[] parameters;
}Copy to clipboardErrorCopied

validateMethodMapping

  • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#validateMethodMapping

HandlerMethod 进行验证

private void validateMethodMapping(HandlerMethod handlerMethod, T mapping) {
   // Assert that the supplied mapping is unique.
   // 从缓存中获取
   HandlerMethod existingHandlerMethod = this.mappingLookup.get(mapping);
   // 是否为空 , 是否相同
   if (existingHandlerMethod != null && !existingHandlerMethod.equals(handlerMethod)) {
      throw new IllegalStateException(
            "Ambiguous mapping. Cannot map '" + handlerMethod.getBean() + "' method \n" +
                  handlerMethod + "\nto " + mapping + ": There is already '" +
                  existingHandlerMethod.getBean() + "' bean method\n" + existingHandlerMethod + " mapped.");
   }
}Copy to clipboardErrorCopied

getDirectUrls

  • 找到 mapping 匹配的 url

  • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getDirectUrls

private List<String> getDirectUrls(T mapping) {
   List<String> urls = new ArrayList<>(1);
   // mapping.getPatternsCondition().getPatterns()
   for (String path : getMappingPathPatterns(mapping)) {
      // 是否匹配
      if (!getPathMatcher().isPattern(path)) {
         urls.add(path);
      }
   }
   return urls;
}Copy to clipboardErrorCopied

handlerMethod 和 name 绑定

String name = null;
if (getNamingStrategy() != null) {
   // 获取名字
   // 类名#方法名
   name = getNamingStrategy().getName(handlerMethod, mapping);
   // 设置 handlerMethod + name 的关系
   addMappingName(name, handlerMethod);
}Copy to clipboardErrorCopied
  • org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMethodMappingNamingStrategy#getName
@Override
public String getName(HandlerMethod handlerMethod, RequestMappingInfo mapping) {
   if (mapping.getName() != null) {
      return mapping.getName();
   }
   StringBuilder sb = new StringBuilder();
   // 短类名
   String simpleTypeName = handlerMethod.getBeanType().getSimpleName();
   for (int i = 0; i < simpleTypeName.length(); i++) {
      if (Character.isUpperCase(simpleTypeName.charAt(i))) {
         sb.append(simpleTypeName.charAt(i));
      }
   }
   // 组装名称
   // 类名+#+方法名称
   sb.append(SEPARATOR).append(handlerMethod.getMethod().getName());
   return sb.toString();
}Copy to clipboardErrorCopied

initCorsConfiguration

  • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initCorsConfiguration
@Override
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
   // 创建 handlerMethod
   HandlerMethod handlerMethod = createHandlerMethod(handler, method);
   // 获取 beanType
   Class<?> beanType = handlerMethod.getBeanType();
   // 获取跨域注解 CrossOrigin
   CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(beanType, CrossOrigin.class);
   CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);

   if (typeAnnotation == null && methodAnnotation == null) {
      return null;
   }

   // 跨域信息配置
   CorsConfiguration config = new CorsConfiguration();
   // 更新跨域配置
   updateCorsConfig(config, typeAnnotation);
   updateCorsConfig(config, methodAnnotation);

   if (CollectionUtils.isEmpty(config.getAllowedMethods())) {
      // 跨域配置赋给方法
      for (RequestMethod allowedMethod : mappingInfo.getMethodsCondition().getMethods()) {
         config.addAllowedMethod(allowedMethod.name());
      }
   }
   // 应用跨域
   return config.applyPermitDefaultValues();
}Copy to clipboardErrorCopied

unregister

  • org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#unregister

    移除 mapping 信息

  • 执行 map , list 相关的移除方法.

public void unregister(T mapping) {
   this.readWriteLock.writeLock().lock();
   try {
      MappingRegistration<T> definition = this.registry.remove(mapping);
      if (definition == null) {
         return;
      }

      this.mappingLookup.remove(definition.getMapping());

      for (String url : definition.getDirectUrls()) {
         List<T> list = this.urlLookup.get(url);
         if (list != null) {
            list.remove(definition.getMapping());
            if (list.isEmpty()) {
               this.urlLookup.remove(url);
            }
         }
      }

      removeMappingName(definition);

      this.corsLookup.remove(definition.getHandlerMethod());
   }
   finally {
      this.readWriteLock.writeLock().unlock();
   }
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。