[SpringMVC源码学习]请求如何处理(六)

106 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   Object handler = getHandlerInternal(request);
   if (handler == null) {
      handler = getDefaultHandler();
   }
   if (handler == null) {
      return null;
   }
   // Bean name or resolved handler?
   if (handler instanceof String) {
      String handlerName = (String) handler;
      handler = obtainApplicationContext().getBean(handlerName);
   }
​
   // Ensure presence of cached lookupPath for interceptors and others
   if (!ServletRequestPathUtils.hasCachedPath(request)) {
      initLookupPath(request);
   }
​
   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
​
   if (logger.isTraceEnabled()) {
      logger.trace("Mapped to " + handler);
   }
   else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
      logger.debug("Mapped to " + executionChain.getHandler());
   }
​
   if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
      CorsConfiguration config = getCorsConfiguration(handler, request);
      if (getCorsConfigurationSource() != null) {
         CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
         config = (globalConfig != null ? globalConfig.combine(config) : config);
      }
      if (config != null) {
         config.validateAllowCredentials();
      }
      executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
   }
​
   return executionChain;
}

首先,就去调用getHandlerInternal方法去获得处理器,其实就是对应的处理方法。

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
   // 根据请求得到对应的映射路径,比如:"/hello"
   String lookupPath = initLookupPath(request);
   Object handler;
   if (usesPathPatterns()) {  // 如果此 HandlerMapping 已启用以使用已解析的 PathPatterns,则返回“true”。
      RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
      handler = lookupHandler(path, lookupPath, request);
   }
   else {
      // 通过请求的映射路径找到对应的处理方法及方法上面@RequestMapping注解对应的RequestMappingInfo对象
      handler = lookupHandler(lookupPath, request);
   }
   if (handler == null) {
      // We need to care for the default handler directly, since we need to
      // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
      Object rawHandler = null;
      if (StringUtils.matchesCharacter(lookupPath, '/')) {
         rawHandler = getRootHandler();
      }
      if (rawHandler == null) {
         rawHandler = getDefaultHandler();
      }
      if (rawHandler != null) {
         // Bean name or resolved handler?
         if (rawHandler instanceof String) {
            String handlerName = (String) rawHandler;
            rawHandler = obtainApplicationContext().getBean(handlerName);
         }
         validateHandler(rawHandler, request);
         handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
      }
   }
   return handler;
}

首先就根据request拿到对应的请求路径,然后把他设置到request中。然后根据这个映射路径去找对应的处理方法,我们前面应该也提到,有两个list集合存储了url,method,和RequestMappingInfo的对应关系。那么我们看看到底是不是这样找到的。进入到lookupHandler方法。

// 查找给定 URL 路径的处理程序实例。当使用 PathMatcher 进行字符串模式匹配时使用此方法。
protected Object lookupHandler(String lookupPath, HttpServletRequest request) throws Exception {
   // 这里直接能拿到 RequestMappingInfo 的对应关系
   Object handler = getDirectMatch(lookupPath, request);
   if (handler != null) {
      return handler;
   }
   ...
}

由于我们handler不为空,我们返回到getHandler方法下,直接来到getHandlerExecutionChain(handler, request)方法下,去拿到HandlerExecutionChain,也就是处理器执行链了。

上面我是通过实现Controller接口来到这个方法里面,但是如果通过注解@RequestMappinggetHandlerInternal是进入下面的方法

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
   // 根据请求得到对应的映射路径
   String lookupPath = initLookupPath(request);
   this.mappingRegistry.acquireReadLock();
   try {
      //通过请求的映射路径找到对应的处理方法及方法上面@RequestMapping注解对应的RequestMappingInfo对象
      HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
      return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
   }
   finally {
      this.mappingRegistry.releaseReadLock();
   }
}

而这里则是通过lookupHandlerMethod拿到RequestMappingInfo的对应关系,有两个map集合存储,不是上面的list了,

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
   List<Match> matches = new ArrayList<>();
   List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
   if (directPathMatches != null) {
      addMatchingMappings(directPathMatches, matches, request);
   }
   if (matches.isEmpty()) {
      addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
   }
   if (!matches.isEmpty()) {
      Match bestMatch = matches.get(0);
      if (matches.size() > 1) {
         Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
         matches.sort(comparator);
         bestMatch = matches.get(0);
         if (logger.isTraceEnabled()) {
            logger.trace(matches.size() + " matching mappings: " + matches);
         }
         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) {
               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 handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
   }
}

刚进入这个方法,我们就看到一个Match对象,这个对象是什么呢。

image-20220807110656869

其实这个对象就是将方法和方法上的注解对象进行了一个包装而已。了解了这个之后,我们继续,它会创建一个list集合,用来存储Match对象,接着去url去拿到对应的RequestMappingInfo对象集合。

public List<T> getMappingsByDirectPath(String urlPath) {
   return this.pathLookup.get(urlPath);
}

这里我们就很清楚了,就是从我们以前的urlLookup中去找,找到对应的RequestMappingInfo。然后返回。找到之后,会把他放到一个个的match中,然后再由matches存储起来。我们来看看是怎么存储的。

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, this.mappingRegistry.getRegistrations().get(mapping)));
      }
   }
}

依然是去遍历mappings,那么getMatchingMapping这个方法是干什么呢,其实这个方法就是去重新包装RequestMappingInfo,把request中的参数都放到RequestMappingInfo中,有兴趣的可以去看看。包装完成后,就会把这个RequestMappingInfo和他对应的方法包装为一个个match被matches所存起来。那么它对应的方法是怎么来的呢。getMappings这个方法,返回了一个map,可以看到这个map就是当初我们存储RequestMappingInfo和method的map,通过它就可以找到对应的method了。存储好之后,经过一些算法找到比较好的那个处理方法,然后返回。返回到getHandlerInternal当中,拿到对应的HandlerMethod对应的处理方法之后,然后再通过createWithResolvedBean方法给HandlerMethod设置一下他所对应的类之后,将HandlerMethod返回。

上面通过注解实现结束

然后一直返回到getHandler当中。此时我们已经拿到了request对应的处理方法,然后就要去拿到HandlerExecutionChain,也就是处理器链了。

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   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;
}

其实就是把定义的拦截器放到我们执行链中,我们把我们的拦截器存放在adaptedInterceptors的List下,此时我们没有配置,所以adaptedInterceptors的·长度为0,直接跳过增强for循环,然后返回,返回到getHandler当中,拿到处理器执行链,然后进行一些跨域的处理之后,返回执行链。返回到doDispatch当中。然后再去拿到处理器设配器

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   if (this.handlerAdapters != null) {
      for (HandlerAdapter adapter : this.handlerAdapters) {
         if (adapter.supports(handler)) {
            return adapter;
         }
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

依然是遍历查找,选择支持当前类新的Adapter,然后返回到doDispatch当中,我这里debug里看到的是4个类,正是我们从DispatcherServlet.properties实例化HandlerAdapters的类,我拿到的是SimpleControllerHandlerAdapter。拿到之后,请求的处理类型,以get为例,开始判断是不是get方法 并且是否被修改(修改说明不安全了,直接返回),然后进入下面的判断,是拦截器的的流程

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
   return;
}
// 实际上调用处理程序
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

拦截器的实现方法applyPreHandle返回false的时候,那么就不会继续执行,而是返回,只有他返回true的时候才会继续执行处理方法等等。我们主要来看一看他是怎么执行处理方法的。

// 应用注册拦截器的 preHandle 方法。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
   for (int i = 0; i < this.interceptorList.size(); i++) {
      HandlerInterceptor interceptor = this.interceptorList.get(i);
      if (!interceptor.preHandle(request, response, this.handler)) {
         triggerAfterCompletion(request, response, null);
         return false;
      }
      this.interceptorIndex = i;
   }
   return true;
}

执行之后handle方法处理

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
​
   return ((Controller) handler).handleRequest(request, response);
}

->

public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","abc");
        mv.setViewName("hello");
        return mv;
    }
​
​
}

一下子来到了我自己实现的Controller类进行处理,这个是不通过注解形式的,我们再看看我们的注解

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
​
   return handleInternal(request, response, (HandlerMethod) handler);
}

->

protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
​
   ModelAndView mav;
   checkRequest(request);
​
   // Execute invokeHandlerMethod in synchronized block if required.
   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
         // No HttpSession available -> no mutex necessary
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
      // No synchronization on session demanded at all...
      mav = invokeHandlerMethod(request, response, handlerMethod);   // <-- 这里
   }
​
   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
         applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      }
      else {
         prepareResponse(response);
      }
   }
​
   return mav;
}

invokeHandlerMethod方法中会去调用invokeAndHandle方法去反射调用处理方法。我们直接看这个方法。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
      ...
          
      invocableMethod.invokeAndHandle(webRequest, mavContainer); // <- 这里
      if (asyncManager.isConcurrentHandlingStarted()) {
         return null;
      }
​
      return getModelAndView(mavContainer, modelFactory, webRequest);
   }
   finally {
      webRequest.requestCompleted();
   }
}

进入invokeAndHandle里面,主要就是invokeForRequest方法

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
​
   Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // <-- 这里
   setResponseStatus(webRequest);
​
   ...
}

进来看invokeForRequest方法

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
​
   Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
   if (logger.isTraceEnabled()) {
      logger.trace("Arguments: " + Arrays.toString(args));
   }
   return doInvoke(args);
}

拿到参数,doInvoke反射调用处理方法。但是getMethodArgumentValues是怎么匹配这些参数的呢。

// 参数绑定
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
   // 得到参数数组
   MethodParameter[] parameters = getMethodParameters();
   if (ObjectUtils.isEmpty(parameters)) {
      return EMPTY_ARGS;
   }
​
   Object[] args = new Object[parameters.length];
   for (int i = 0; i < parameters.length; i++) {
      MethodParameter parameter = parameters[i];
      // 得到参数具体的名字
      parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
      // 是否提供相应参数的注解,一般是不提供的 为null
      args[i] = findProvidedArgument(parameter, providedArgs);
      if (args[i] != null) {
         continue;
      }
      // 拿到对应的参数解析器
      if (!this.resolvers.supportsParameter(parameter)) {
         throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
      }
      try {
         args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
      }
      catch (Exception ex) {
         // Leave stack trace for later, exception may actually be resolved and handled...
         if (logger.isDebugEnabled()) {
            String exMsg = ex.getMessage();
            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
               logger.debug(formatArgumentError(parameter, exMsg));
            }
         }
         throw ex;
      }
   }
   return args;
}

supportsParameter这个方法我们要注意一下,用来拿到对应的参数解析器。

image-20220807161021673

进入到getArgumentResolver方法,遍历argumentResolvers,因为参数解析器有很多,这里使用的的是RequestParamMethodArgumentResolver类。

public boolean supportsParameter(MethodParameter parameter) {
   if (parameter.hasParameterAnnotation(RequestParam.class)) {
      if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
         RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
         return (requestParam != null && StringUtils.hasText(requestParam.name()));
      }
      else {
         return true;
      }
   }
   else {
      if (parameter.hasParameterAnnotation(RequestPart.class)) {
         return false;
      }
      parameter = parameter.nestedIfOptional();
      if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
         return true;
      }
      else if (this.useDefaultResolution) {
         return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
      }
      else {
         return false;
      }
   }
}

再到这个方法,看看参数是否有RequestParam注解,如果有,在看看RequestParam是不是有参数,有的话返回到getArgumentResolver中,将参数和解析器放到缓存中,便于我们下次使用。最后返回到getMethodArgumentValues方法中。下一步就应该resolveArgument方法里去解析参数了。

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
​
   HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
   if (resolver == null) {
      throw new IllegalArgumentException("Unsupported parameter type [" +
            parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
   }
   return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

\