doDispatch方法中的getHandler细节

158 阅读7分钟

在DispatcherServlet的onRefresh方法打个断点,调用get接口:

@Override
protected void onRefresh(ApplicationContext context) {
   initStrategies(context);
}

image.png

可以看到,Tomcat会调用Servlet接口的init方法,就像前面Tomcat调用了Service方法一样;也可以在yml里调整spring.mvc.servlet.load-on-startup: 1,这样就会在运行时进行初始化,负数时就是懒加载,首次调用才会加载;

protected void initStrategies(ApplicationContext context) {
   initMultipartResolver(context);
   initLocaleResolver(context);
   initThemeResolver(context);
   initHandlerMappings(context);
   initHandlerAdapters(context);
   initHandlerExceptionResolvers(context);
   initRequestToViewNameTranslator(context);
   initViewResolvers(context);
   initFlashMapManager(context);
}

this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
Map<String, HandlerAdapter> matchingBeans =       BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);

每个点进去,都可以发现,都是从Spring的bean工厂去拿,要不然就是在catch中取默认的, this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);就是取jar包下DispatcherServlet.properties,类似之前的spring.factories;

image.png

那么,从spring的bean工厂拿的是什么时候进入到这个bean工厂去的呢?

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class })
@ConditionalOnProperty(prefix = "spring.servlet.multipart", name = "enabled", matchIfMissing = true)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {

   private final MultipartProperties multipartProperties;

   public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
      this.multipartProperties = multipartProperties;
   }

   @Bean
   @ConditionalOnMissingBean({ MultipartConfigElement.class, CommonsMultipartResolver.class })
   public MultipartConfigElement multipartConfigElement() {
      return this.multipartProperties.createMultipartConfig();
   }

   @Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
   @ConditionalOnMissingBean(MultipartResolver.class)
   public StandardServletMultipartResolver multipartResolver() {
      StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
      multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
      return multipartResolver;
   }
}

差不多全都是利用@Configuration+@Bean注入到spring的bean工厂去的,其他的自己慢慢找吧,WebMvcAutoConfiguration也有很多;

值得注意的是initHandlerMappings(context);
这里边往this.handlerMappings中添加了HandlerMapping类型的类,这就使doDispatch方法中的 mappedHandler = getHandler(processedRequest);中的handlerMappings不为空,然后循环处理,其中处理@RequestMapping,把对应的处理器方法handler和注解中的value对应起来关系加入到

image.png

从图中可以看到AbstractHandlerMethodMapping这个类实现了InitializingBean,这是spring的一个预留口,实例化的时候会调用的;
然后找到它的实现方法:

RequestMappingHandlerMapping中可以看到:

public void afterPropertiesSet() {

   this.config = new RequestMappingInfo.BuilderConfiguration();
   this.config.setTrailingSlashMatch(useTrailingSlashMatch());
   this.config.setContentNegotiationManager(getContentNegotiationManager());

   if (getPatternParser() != null) {
      this.config.setPatternParser(getPatternParser());
      Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
            "Suffix pattern matching not supported with PathPatternParser.");
   }
   else {
      this.config.setSuffixPatternMatch(useSuffixPatternMatch());
      this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
      this.config.setPathMatcher(getPathMatcher());
   }

   super.afterPropertiesSet();
}

点击这个super.afterPropertiesSet();

public void afterPropertiesSet() {
   initHandlerMethods();
}

/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 * @see #getCandidateBeanNames()
 * @see #processCandidateBean
 * @see #handlerMethodsInitialized
 */
protected void initHandlerMethods() {
   for (String beanName : getCandidateBeanNames()) {
      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
         processCandidateBean(beanName);
      }
   }
   handlerMethodsInitialized(getHandlerMethods());
}

这里有个getCandidateBeanNames()方法,点进去可以看到,其实就是从spring的bean工厂中取出所有Object.class的bean的名字;

然后看到进入if,

protected void processCandidateBean(String beanName) {
   Class<?> beanType = null;
   try {
      beanType = obtainApplicationContext().getType(beanName);
   }
   catch (Throwable ex) {
      // An unresolvable bean type, probably from a lazy bean - let's ignore it.
      if (logger.isTraceEnabled()) {
         logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
      }
   }
   if (beanType != null && isHandler(beanType)) {
      detectHandlerMethods(beanName);
   }
}

最下面这个if中有个isHandler方法,可以看到

protected boolean isHandler(Class<?> beanType) {
   return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
         AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

 

然后到这个方法:

protected void detectHandlerMethods(Object handler) {
   Class<?> handlerType = (handler instanceof String ?
         obtainApplicationContext().getType((String) handler) : handler.getClass());

   if (handlerType != null) {
      Class<?> userType = ClassUtils.getUserClass(handlerType);
      Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
            (MethodIntrospector.MetadataLookup<T>) method -> {
               try {
                  return getMappingForMethod(method, userType);
               }
               catch (Throwable ex) {
                  throw new IllegalStateException("Invalid mapping on handler class [" +
                        userType.getName() + "]: " + method, ex);
               }
            });
      if (logger.isTraceEnabled()) {
         logger.trace(formatMappings(userType, methods));
      }
      else if (mappingsLogger.isDebugEnabled()) {
         mappingsLogger.debug(formatMappings(userType, methods));
      }
      methods.forEach((method, mapping) -> {
         Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
         registerHandlerMethod(handler, invocableMethod, mapping);
      });
   }
}

这里有个getMappingForMethod,就是获取@RequestMapping注解里面的值,然后调用最底下的

registerHandlerMethod(handler, invocableMethod, mapping);

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
   this.mappingRegistry.register(mapping, handler, method);
}

可以看到,被@RequestMapping和@Controller注解的方法会被加入到mappingRegistry这里边;

回到doDispatch方法中来,之前的getHandler如何存储handler和@RequestMapping中value的关系就知道了;其实就是在一个map中;

那么下面就是通过url如何匹配最合适的handler了:
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;
}

image.png

Object handler = getHandlerInternal(request);第一行获取handler;

子类:

//获取请求路径信息,其实就是获取url信息

String lookupPath = initLookupPath(request);

//根据路径信息查找最佳匹配的处理器方法

HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {

//创建匹配结果列表,用于保存多个匹配结果,Match为匹配的映射信息与处理器方法的封装
   List<Match> matches = new ArrayList<>();

//首先尝试通过映射注册器根据请求路径获取直接对应的映射信息列表
   List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
   if (directPathMatches != null) {

//如果不为空,调用条件匹配映射的方法,内部首先遍历映射信息列表

//判断当前请求与遍历中的映射信息是否匹配,如果匹配,则把匹配的映射信息与处理器方法封装为Match匹配结果添加匹配结果列表中;
      addMatchingMappings(directPathMatches, matches, request);
   }
   if (matches.isEmpty()) {

//如果不能通过上边的路径直接映射获取匹配结果,则尝试遍历全部映射信息,执行与上面相同的获取匹配结果的逻辑
      addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);\
   }

//如果匹配结果不为空
   if (!matches.isEmpty()) {

//获取匹配列表中的第一个元素,暂时视为最佳匹配
      Match bestMatch = matches.get(0);

//如果匹配列表大于1的话
      if (matches.size() > 1) {

//创建一个匹配结果比较器,用于获取最佳匹配,比较逻辑为直接使用匹配信息进行比较\
         Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));

//通过比较器排序,排序靠前的匹配度最高,其实就是compare他们的mapping信息,headers,content-Type之类的,有个先后顺序,HEAD > pathPatternsCondition > paramsCondition > headersCondition等等
         matches.sort(comparator);

//获取匹配列表中第一个元素,视为最佳匹配
         bestMatch = matches.get(0);
         if (logger.isTraceEnabled()) {
            logger.trace(matches.size() + " matching mappings: " + matches);
         }

//如果是CORS预检请求,则直接返回一个预定义好的处理器方法,忽略预检请求多个匹配结果异常,即允许遇见请求有多个匹配,不影响后续处理
         if (CorsUtils.isPreFlightRequest(request)) {
            for (Match match : matches) {
               if (match.hasCorsConfig()) {
                  return PREFLIGHT_AMBIGUOUS_MATCH;
               }
            }
         }
         else {

//非预检请求,再获取匹配列表中第二个元素进行比较
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {

//如果为0,则表示至少两个最佳匹配
               Method m1 = bestMatch.getHandlerMethod().getMethod();
               Method m2 = secondBestMatch.getHandlerMethod().getMethod();
               String uri = request.getRequestURI();

//如果多个最佳匹配,则抛出不明确(不唯一)的处理器方法映射异常
               throw new IllegalStateException(
                     "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
            }
         }
      }
      request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());

//如果只有一个最佳匹配,则执行处理匹配方法,在处理匹配方法中可以执行一些逻辑
      handleMatch(bestMatch.mapping, lookupPath, request);

//返回最佳匹配对应的处理器方法
      return bestMatch.getHandlerMethod();
   }
   else {

//如果没有匹配结果,执行无匹配方法,可执行一些特殊逻辑,其实就是return null
      return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
   }
}

这个bestMatch.getHandlerMethod()其实就是刚刚放入mappingRegistry中的一个数据结构,封装了handler和method,这个handler其实就是bean的名字beanName

new HandlerMethod(handler, method);

到这我们就完成了handler的获取了,下面就是拦截链是怎么和handler绑定在一起的了;
在Object handler = getHandlerInternal(request),这时候这个handler就是HandlerMethod了。下面有个HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
最终,返回的就是doDispatch方法中的getHandler就是它了;

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {

//如果已经是处理器执行链,则不做处理;如果不是,则封装为处理器执行链。因为在获取处理器逻辑中可能已经封装为处理器执行链了,其他非我们这条路线的mapping,除了@RequestMapping还有其他的
   HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
springmvc拦截器使用         (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

   for (HandlerInterceptor interceptor : this.adaptedInterceptors) {

//如果拦截器类型为MappedInterceptor
      if (interceptor instanceof MappedInterceptor) {
      
         MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
         
//根据其匹配逻辑判断请求路径是否与拦截器配置的路径模式匹配
         if (mappedInterceptor.matches(request)) {
            chain.addInterceptor(mappedInterceptor.getInterceptor());
         }
      }
      else {

//如果不是mappedInterceptor ,则视为拦截全部路径请求,添加到处理器执行链中
         chain.addInterceptor(interceptor);
      }
   }
   return chain;
}

到此,完整的HandlerExecutionChain 就有了

那么问题来了,这个this.adaptedInterceptors拦截器是什么时候在这个类里的:

还得从RequestMappingHandlerMapping 被放入spring的factory中说去,WebMvcConfigurationSupport中说起

@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
      @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
      @Qualifier("mvcConversionService") FormattingConversionService conversionService,
      @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

   RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
   mapping.setOrder(0);
   mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
   mapping.setContentNegotiationManager(contentNegotiationManager);
   mapping.setCorsConfigurations(getCorsConfigurations());

   PathMatchConfigurer pathConfig = getPathMatchConfigurer();
   if (pathConfig.getPatternParser() != null) {
      mapping.setPatternParser(pathConfig.getPatternParser());
   }
   else {
      mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
      mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());

      Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
      if (useSuffixPatternMatch != null) {
         mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
      }
      Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
      if (useRegisteredSuffixPatternMatch != null) {
         mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
      }
   }
   Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
   if (useTrailingSlashMatch != null) {
      mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
   }
   if (pathConfig.getPathPrefixes() != null) {
      mapping.setPathPrefixes(pathConfig.getPathPrefixes());
   }

   return mapping;
}

可以看到mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));

protected final Object[] getInterceptors(
      FormattingConversionService mvcConversionService,
      ResourceUrlProvider mvcResourceUrlProvider) {

   if (this.interceptors == null) {
      InterceptorRegistry registry = new InterceptorRegistry();
      addInterceptors(registry);
      registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
      registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
      this.interceptors = registry.getInterceptors();
   }
   return this.interceptors.toArray();
}

addInterceptors(registry);这里:

来到子类DelegatubgWebMvcConfiguration的

protected void addInterceptors(InterceptorRegistry registry) {
   this.configurers.addInterceptors(registry);
}

public void addInterceptors(InterceptorRegistry registry) {
   for (WebMvcConfigurer delegate : this.delegates) {
      delegate.addInterceptors(registry);
   }
}

我自己写的拦截器就在这里面

image.png

image.png

有个细节:
就是我们写的都是带有exclude和include,且还是HandlerInterceptor类型的拦截器,那么我们可以看到,形成拦截器链的时候,应该要经过MappedInterceptor 的matches才对啊,那么HandlerInterceptor是什么时候变成了MappedInterceptor 的呢?

答案就是上面方法getInterceptors中的this.interceptors = registry.getInterceptors();

protected List<Object> getInterceptors() {
   return this.registrations.stream()
         .sorted(INTERCEPTOR_ORDER_COMPARATOR)
         .map(InterceptorRegistration::getInterceptor)
         .collect(Collectors.toList());
}

在map中,点击getInterceptor,可以看到,就是经过这里形成了MappedInterceptor 的

protected Object getInterceptor() {

   if (this.includePatterns == null && this.excludePatterns == null) {
      return this.interceptor;
   }

   MappedInterceptor mappedInterceptor = new MappedInterceptor(
         StringUtils.toStringArray(this.includePatterns),
         StringUtils.toStringArray(this.excludePatterns),
         this.interceptor);

   if (this.pathMatcher != null) {
      mappedInterceptor.setPathMatcher(this.pathMatcher);
   }

   return mappedInterceptor;
}

那么问题来了:它只是把HandlerInterceptor放入到this.interceptors中,我们看到的可是this.adaptedInterceptors这个喔,那么前者是什么时候放入到后者的呢?

其实就是在spring实例化getBean RequestMappingHandlerMapping 的时候被一个后置处理器ApplicationContextAwareProcessor处理了,我们可以在AbstractHandlerMapping类的方法上打断点,可以清晰的看到


protected void initApplicationContext() throws BeansException {
   extendInterceptors(this.interceptors);
   detectMappedInterceptors(this.adaptedInterceptors);
   initInterceptors();
}

image.png


protected void initInterceptors() {
   if (!this.c.isEmpty()) {
      for (int i = 0; i < this.interceptors.size(); i++) {
         Object interceptor = this.interceptors.get(i);
         if (interceptor == null) {
            throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
         }
         this.adaptedInterceptors.add(adaptInterceptor(interceptor));
      }
   }
}
可以看到interceptors的拦截器装入到了adaptedInterceptors中。
到这里就是完整的HandlerExecutionChain了;