第一篇:HandlerMapping源码解读

前言

想必读了《理解SpringMVC看这一篇就够了》,你已经了解到DispatchServlet中的initStrategies()方法,初始化HandlerMapping组件,那让我们一切从这里开始吧!!!以RequestMappingHandlerMapping为例。

image.png

第一步:Spring创建HandlerMapping的Bean实例

简单了解一下,Spring创建Bean实例的过程中,在最终bean之前,如果实现了InitializingBean接口,那么就会调用afterPropertiesSet()方法。

// RequestMappingHandlerMapping重写了父类的afterPropertiesSet方法。当完成Bean之前,
// 会调用afterPropertiesSet()
@Override
public void afterPropertiesSet() {
   // 调用父类的afterPropertiesSet()方法super.afterPropertiesSet();
}

// 
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    public void afterPropertiesSet() {
       ② initHandlerMethods();
    }

    protected void initHandlerMethods() {
       for (String beanName : getCandidateBeanNames()) {
          if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
             // 处理容器中的bean,如何处理呢????
             ③ processCandidateBean(beanName);
          }
       }
       handlerMethodsInitialized(getHandlerMethods());
    }
    //  处理容器中bean的具体逻辑
    protected void processCandidateBean(String beanName) {
       Class<?> beanType = null;
       try {
          beanType = obtainApplicationContext().getType(beanName);
       }
       // isHandler():判断bean是否有@Controller 或者 @RequestMapping 注解
       if (beanType != null && isHandler(beanType)) {
          // 对加了注解的bean进行监测
          ④ detectHandlerMethods(beanName);
       }
    }
    // 
    protected void detectHandlerMethods(Object handler) {
       Class<?> handlerType = (handler instanceof String ?
             obtainApplicationContext().getType((String) handler) : handler.getClass());

       if (handlerType != null) {
          Class<?> userType = ClassUtils.getUserClass(handlerType);
          /**
          获取当前Controller中所有标注了@RequestMapping的方法表Map<Method,RequestMappingInfo>。
          eg:key=com.shang.controller.HelloController.hello()
              value={RequestMappingInfo}
          */
          Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> {
                   try {
                      ⑤ return getMappingForMethod(method, userType);
                   }
                   catch (Throwable ex) {
                   }
                });
          //        
          methods.forEach((method, mapping) -> {
             Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
             // 添加到MappingRegistry(映射注册表)
             ⑥ registerHandlerMethod(handler, invocableMethod, mapping);
          });
       }
    }
}

复制代码

第一步的目的,先获取标注了@Controller或者@RequestMapping注解的Controller类,然后解析Controller中的所有方法,提取出标注了@RequestMapping注解的方法,得到Map<method,requestMappingInfo>集合;最后将Map集合遍历添加到MappingRegistry(映射注册表),映射注册表才是我们最终需要的答案。
image.png

第二步:获取所有实现HandlerMapping接口的Bean实例

private List<HandlerMapping> handlerMappings; // 存放所有的HandlerMapping
private void initHandlerMappings(ApplicationContext context) {
   // detectAllHandlerMappings默认值为true,程序员可手动设置
   if (this.detectAllHandlerMappings) {
      // 在所有容器(包括父容器)中查找所有实现HandlerMapping接口的实现类。
      Map<String, HandlerMapping> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
      if (!matchingBeans.isEmpty()) {
         // 赋值排序
         this.handlerMappings = new ArrayList<>(matchingBeans.values());
         AnnotationAwareOrderComparator.sort(this.handlerMappings);
      }
   }
   else {
      try {
         // 从容器中获取beanName="handlerMapping"的Bean实例,赋值给 handlerMappings
         HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
         this.handlerMappings = Collections.singletonList(hm);
      }
   }
   // 如果开发人员没有配置HandlerMapping,那么就从DispatcherServlet.properties文件中
   // 加载默认的HandlerMapping。
   if (this.handlerMappings == null) {
      // 此方法中有很多好用的工具类,可以看一看
      this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
   }
}
复制代码

最终目的:获取容器(包括所有父容器)中的HandlerMapping的实现类,添加到List<HandlerMapping>,以供使用。
image.png

第三步:使用HandlerMapping的Bean实例

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
      for (HandlerMapping mapping : this.handlerMappings) {
         HandlerExecutionChain handler = mapping.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   return null;
}
// AbstractHandlerMapping.java
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   // 根据request获取最合适的HandlerMethod,什么是最合适呢???
   Object handler = getHandlerInternal(request);
   // 根据request获取所有匹配的拦截器,最终与handler method组成一个HandlerExecutionChain
   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
   return executionChain;
}

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   // new HandlerExecutionChain对象,一般是原生handler
   HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
         (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

   for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
      if (interceptor instanceof MappedInterceptor) {
         MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
         if (mappedInterceptor.matches(request)) {
            chain.addInterceptor(mappedInterceptor.getInterceptor());
         }
      }
      else {
         chain.addInterceptor(interceptor);
      }
   }
   return chain;
}

复制代码

目的之一:获取最合适的HandlerMethod。

@RequestMapping(value = "/hello", method = {RequestMethod.POST})
public String hello3() {
    return "hello";
}
@RequestMapping(value = "/hello", method = {RequestMethod.GET})
public String hello4() {
    return "hello";
}
复制代码

以上两个都是/hello请求,映射注册表中,pathLookup和nameLookup中,ArrayList的size为2。
当浏览器请求/hello时,如何抉择呢?最合适还是需要通过RequestMappingInfo来决定。比如,先比较value,再比较method...当然RequestMappingInfo参数远远不仅仅这两个。

目的之二:获取HandlerExecutionChain

将Handler Method 和 HandlerInterceptor 封装成HandlerExecutionChain,供DispatchServlet使用。

总结

image.png