SpringMvc 源码分析-注册Mapping

SpringBoot 默认开启扫描@Configuration,所以DelegatingWebMvcConfiguration默认会被扫进来,因为继承WebMvcConfigurationSupport,所以也会被实例化

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {...}

在WebMvcConfigurationSupport里面声明了
requestMappingHandlerMapping的Bean,我们今天就从他开始

/**
 * Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping
 * requests to annotated controllers.
 */
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
   RequestMappingHandlerMapping handlerMapping = createRequestMappingHandlerMapping();  //创建
   handlerMapping.setOrder(0);
   handlerMapping.setInterceptors(getInterceptors());
   handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
   handlerMapping.setCorsConfigurations(getCorsConfigurations());

   PathMatchConfigurer configurer = getPathMatchConfigurer();
   if (configurer.isUseSuffixPatternMatch() != null) {
      handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
   }
   if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
      handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
   }
   if (configurer.isUseTrailingSlashMatch() != null) {
      handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
   }
   if (configurer.getPathMatcher() != null) {
      handlerMapping.setPathMatcher(configurer.getPathMatcher());
   }
   if (configurer.getUrlPathHelper() != null) {
      handlerMapping.setUrlPathHelper(configurer.getUrlPathHelper());
   }

   return handlerMapping;
}

我们可以看一下这个类是如何实现的。RequestMappingHandlerMapping 这个类实现了InitializingBean,所以关注一下他的生命周期方法afterPropertiesSet

@Override
public void afterPropertiesSet() {
   this.config = new RequestMappingInfo.BuilderConfiguration();
   this.config.setPathHelper(getUrlPathHelper());
   this.config.setPathMatcher(getPathMatcher());
   this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
   this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
   this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
   this.config.setContentNegotiationManager(getContentNegotiationManager());

   super.afterPropertiesSet();  //调用父类的生命周期方法
}

他的父类是
AbstractHandlerMethodMapping,
他的方法里面
initHandlerMethods

@Override
public void afterPropertiesSet() {
   initHandlerMethods();
}

是不是有点感觉了,初始化HandlerMethod,我们可以确定就是在这个方法里面,处理我们定义的RequestMapping等注解的。我们详细看一下面的实现

/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 * @see #isHandler(Class)
 * @see #getMappingForMethod(Method, Class)
 * @see #handlerMethodsInitialized(Map)
 */
protected void initHandlerMethods() {
   if (logger.isDebugEnabled()) {
      logger.debug("Looking for request mappings in application context: " + getApplicationContext());
   }
   String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
         BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
         getApplicationContext().getBeanNamesForType(Object.class));

   for (String beanName : beanNames) {
      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
         Class<?> beanType = null;
         try {
            beanType = getApplicationContext().getType(beanName);
         }
         catch (Throwable ex) {
            // An unresolvable bean type, probably from a lazy bean - let's ignore it.
            if (logger.isDebugEnabled()) {
               logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
            }
         }
         if (beanType != null && isHandler(beanType)) {   //这里有个判断,就是我们需要的类
            detectHandlerMethods(beanName);
         }
      }
   }
   handlerMethodsInitialized(getHandlerMethods());
}

因为我们实例化的是
RequestMappingHandlerMapping,所以
isHandler也是他的实现

/**
 * {@inheritDoc}
 * Expects a handler to have a type-level @{@link Controller} annotation.
 */
@Override
protected boolean isHandler(Class<?> beanType) {
   return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
         (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}

在这个Bean上查找Controller与RequestMapping的注解,这也就是我们定义Controller为啥要写上这些注解了。

当我们找到需要处理的类(Controller)后,我们就要解析里面的每个方法了,他们才是真正的处理器。
detectHandlerMethods

/**
 * Look for handler methods in a handler.
 * @param handler the bean name of a handler or a handler instance
 */
protected void detectHandlerMethods(final Object handler) {
   Class<?> handlerType = (handler instanceof String ?
         getApplicationContext().getType((String) handler) : handler.getClass());
   final Class<?> userType = ClassUtils.getUserClass(handlerType);  //找到用户定义的真实的类,防止CGLIB生成的动态代理类

   Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
         new MethodIntrospector.MetadataLookup<T>() {
            @Override
            public T inspect(Method method) {
               return getMappingForMethod(method, userType);   //过滤这个类中的方法,解析RequestMapping
            }
         });

   if (logger.isDebugEnabled()) {
      logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
   }
   for (Map.Entry<Method, T> entry : methods.entrySet()) {
      registerHandlerMethod(handler, entry.getKey(), entry.getValue());
   }
}

我们看一下是怎样过滤的

/**
 * Uses method and type-level @{@link RequestMapping} annotations to create
 * the RequestMappingInfo.
 * @return the created RequestMappingInfo, or {@code null} if the method
 * does not have a {@code @RequestMapping} annotation.
 * @see #getCustomMethodCondition(Method)
 * @see #getCustomTypeCondition(Class)
 */
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
   RequestMappingInfo info = createRequestMappingInfo(method);
   if (info != null) {
      RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
      if (typeInfo != null) {
         info = typeInfo.combine(info);
      }
   }
   return info;
}

/**
 * Delegates to {@link #createRequestMappingInfo(RequestMapping, RequestCondition)},
 * supplying the appropriate custom {@link RequestCondition} depending on whether
 * the supplied {@code annotatedElement} is a class or method.
 * @see #getCustomTypeCondition(Class)
 * @see #getCustomMethodCondition(Method)
 */
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
   RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
   RequestCondition<?> condition = (element instanceof Class<?> ?
         getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
   return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
 
/**
 * Create a {@link RequestMappingInfo} from the supplied
 * {@link RequestMapping @RequestMapping} annotation, which is either
 * a directly declared annotation, a meta-annotation, or the synthesized
 * result of merging annotation attributes within an annotation hierarchy.
 */
protected RequestMappingInfo createRequestMappingInfo(
      RequestMapping requestMapping, RequestCondition<?> customCondition) {

   return RequestMappingInfo
         .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
         .methods(requestMapping.method())
         .params(requestMapping.params())
         .headers(requestMapping.headers())
         .consumes(requestMapping.consumes())
         .produces(requestMapping.produces())
         .mappingName(requestMapping.name())
         .customCondition(customCondition)
         .options(this.config)
         .build();
}

最终的结果就是解析注解变成
RequestMappingInfo类型的对象。解析出来之后我们进行注册

for (Map.Entry<Method, T> entry : methods.entrySet()) {  
   //handler 为controller类
   //key 为 controller中的方法
   // value 为解析出来的RequestMappingInfo类的对象
   registerHandlerMethod(handler, entry.getKey(), entry.getValue());
}
 
/**
 * Register a handler method and its unique mapping. Invoked at startup for
 * each detected handler method.
 * @param handler the bean name of the handler or the handler instance
 * @param method the method to register
 * @param mapping the mapping conditions associated with the handler method
 * @throws IllegalStateException if another method was already registered
 * under the same mapping
 */
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
   this.mappingRegistry.register(mapping, handler, method);
}
 
public void register(T mapping, Object handler, Method method) {
   this.readWriteLock.writeLock().lock();
   try {
      HandlerMethod handlerMethod = createHandlerMethod(handler, method);  //创建HandlerMethod,包含了某个Controller与其中MEthod的信息
      assertUniqueMethodMapping(handlerMethod, mapping);

      if (logger.isInfoEnabled()) {
         logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
      }
      this.mappingLookup.put(mapping, handlerMethod);          //添加映射关系

      List<String> directUrls = getDirectUrls(mapping);
      for (String url : directUrls) {
         this.urlLookup.add(url, mapping);    //可以猜想,请求过来,匹配url,找到mapping,根据mapping找到handlerMethod
      }

      String name = null;
      if (getNamingStrategy() != null) {
         name = getNamingStrategy().getName(handlerMethod, mapping);
         addMappingName(name, handlerMethod);
      }

      CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
      if (corsConfig != null) {
         this.corsLookup.put(handlerMethod, corsConfig);
      }

      this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
   }
   finally {
      this.readWriteLock.writeLock().unlock();
   }
}

以上就是简单的注册机查找的过程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容