六、浅析Spring MVC(源码分析)

45 阅读17分钟

浅析Spring系列

一、浅析Spring启动流程

二、浅析Spring Bean的生命周期

三、浅析Spring循环依赖

四、浅析Spring事件

五、浅析Spring AOP

六、浅析Spring MVC


Spring MVC是Spring系列框架中使用频率最高的部分,因此掌握Spring MVC的处理流程是很重要的,方便实现扩展和问题分析。本文将会简要分析SpringMVC处理的一个请求的流程,抓住主要脉络,了解相关组件,Model相关内容不是本文考虑的重点。Spring整合Tomcat的例子可以查看附录。

1. 概述

Spring提供了DispatcherServlet作为统一的请求入口分发,不论是doGet、doPost等请求,其处理逻辑主要在processRequest中。

其中会先初始化RequestAttributes,并且通过RequestContextHolder进行操作,该对象是个ThreadLocal级别数据。所以在处理完核心的doService逻辑后,最终会进行把RequestAttributes移除。RequestAttributes接口的实现类是ServletRequestAttributes,所以该类中对于属性的设置,其实底层都会设置到HttpServletRequest中。最后也会发现,每个请求执行完,都会发送ServletRequestHandledEvent事件。逻辑的核心在于doService,该方法是个抽象方法,由DispatcherServlet实现。

//FrameworkServlet#processRequest
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   long startTime = System.currentTimeMillis();
   Throwable failureCause = null;

   RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
   ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

   initContextHolders(request, localeContext, requestAttributes);

   try {
      doService(request, response);
   } finally {
      resetContextHolders(request, previousLocaleContext, previousAttributes);
      if (requestAttributes != null) {
         requestAttributes.requestCompleted();
      }
      logResult(request, response, failureCause, asyncManager);
      publishRequestHandledEvent(request, response, startTime, failureCause);
   }
}

在doService逻辑中,会先设置属性到HttpServletRequest中,然后调用doDispatch方法,进行分发逻辑,该方法中承载了整个MVC处理的逻辑。

//DispatcherServlet#doService
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   logRequest(request);

   // Make framework objects available to handlers and view objects.
   request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
   request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
   request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
   request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

   try {
      doDispatch(request, response);
   }
}

doDispatch核心逻辑如下,每个逻辑需要单独解析

  1. 判断是否上传文件,如果有上传文件则需要进行解析出来。
  2. 获取HandlerMapping
  3. 获取HandlerAdapter
  4. 调用HandlerInterceptor接口的preHandle方法
  5. 调用Handler
  6. 调用HandlerInterceptor接口的postHandle方法
  7. 分发结果处理
//DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
		 //1. 是否上传文件
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // Determine handler for the current request.
		 //2. 获取HandlerMapping
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }

         // Determine handler adapter for the current request.
		 //3. 获取HandlerAdapter
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         // Process last-modified header, if supported by the handler.
         String method = request.getMethod();
         boolean isGet = "GET".equals(method);
         if (isGet || "HEAD".equals(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }
		 //4. 调用HandlerInterceptor接口的preHandle方法
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // Actually invoke the handler.
		 //5. 调用Handler
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }

         applyDefaultViewName(processedRequest, mv);
         //6. 调用HandlerInterceptor接口的postHandle方法
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      catch (Throwable err) {
         // As of 4.3, we're processing Errors thrown from handler methods as well,
         // making them available for @ExceptionHandler methods and other scenarios.
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
	  //7. 分发结果处理
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request.
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}

整体处理逻辑如下图所示:和上述逻辑基本一样,现在基本上是前后端分离项目,所以关于视图解析的内容,不再展开。会补充文件解析以及HandlerInterceptor相关的内容。

SpringMVC处理流程.png

2. MultipartResolver

首先会判断是否有MultipartResolver,如果有的话,会调用resolveMultipart方法。在SpringMVC中,默认是没有MultipartResolver的Bean的,如果需要则要自己注入一个名称为“multipartResolver”的MultipartResolver。MultipartResolver有两个实现基于Servlet3.0实现的StandardServletMultipartResolver,以及基于Apache Commons FileUpload 1.2实现的CommonsMultipartResolver

//DispatcherServlet#checkMultipart
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
   if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
      if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
        
      }else {
         try {
            return this.multipartResolver.resolveMultipart(request);
         }
   }
   // If not returned before: return original request.
   return request;
}

以下内容分析StandardServletMultipartResolver,在isMultipart方法中,判断Content-Type是否是“multipart/”开头,如果是则说明是文件上传。

//StandardServletMultipartResolver#isMultipart
public boolean isMultipart(HttpServletRequest request) {
   return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
}

在resolveMultipart方法中,会返回一个StandardMultipartHttpServletRequest

//StandardServletMultipartResolver#resolveMultipart
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
   return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}

StandardMultipartHttpServletRequest也实现了HttpServletRequest接口,构造器会把Request传给父类存储,lazyParsing在默认情况下为false,也就是会做请求的解析。

//StandardMultipartHttpServletRequest
public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
      throws MultipartException {

   super(request);
   if (!lazyParsing) {
      parseRequest(request);
   }
}

parseRequest方法中,会先从Request中获取Part数据,该步骤会触发文件的读取。在Tomcat的实现中,实际会把读取的文件存储到临时文件,然后构造文件信息的Part的返回,业务从Part读取数据,实际是从文件中读取。

然后会遍历Part,获取名称和MultipartFile的映射关系,便于后续环节使用。

parseRequest方法中,实际上会触发网络数据读取,可能会出现时失败,当失败的时候,实际还没有执行到HandlerMapping。

//StandardMultipartHttpServletRequest#parseRequest
private void parseRequest(HttpServletRequest request) {
   try {
      Collection<Part> parts = request.getParts();
      this.multipartParameterNames = new LinkedHashSet<>(parts.size());
      MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
      for (Part part : parts) {
         String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
         ContentDisposition disposition = ContentDisposition.parse(headerValue);
         String filename = disposition.getFilename();
         if (filename != null) {
            if (filename.startsWith("=?") && filename.endsWith("?=")) {
               filename = MimeDelegate.decode(filename);
            }
            files.add(part.getName(), new StandardMultipartFile(part, filename));
         }
         else {
            this.multipartParameterNames.add(part.getName());
         }
      }
      setMultipartFiles(files);
   }
   catch (Throwable ex) {
      handleParseFailure(ex);
   }
}

3. HanderMapping

默认的HanderMapping会有两个,在DispatcherServlet.properties文件中定义,分别是RequestMappingHandlerMapping、BeanNameUrlHandlerMapping

3.1 HandlerMapping定义

首先看一下HandlerMapping如何定义,在使用MVC的功能的时候,一般会启用注解@EnableWebMvc,在该注解中会Import一个DelegatingWebMvcConfiguration类,该类继承于WebMvcConfigurationSupport,会在WebMvcConfigurationSupport中定义一些Bean,其中就包括了HandlerMapping

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

在创建RequestMappingHandlerMapping中会设置order,也会设置两个HandlerInterceptor,分别是ConversionServiceExposingInterceptor和ResourceUrlProviderExposingInterceptor

//WebMvcConfigurationSupport#requestMappingHandlerMapping
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
    mapping.setOrder(0);
    mapping.setInterceptors(getInterceptors());
    return mapping;
}
//WebMvcConfigurationSupport#getInterceptors
protected final Object[] getInterceptors() {
    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();
}

还会创建BeanNameUrlHandlerMapping,至于其他类型的HandlerMapping需要满足一定条件才会进行创建。

//WebMvcConfigurationSupport#beanNameHandlerMapping
@Bean
public BeanNameUrlHandlerMapping beanNameHandlerMapping() {
    BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
    mapping.setOrder(2);
    mapping.setInterceptors(getInterceptors());
    mapping.setCorsConfigurations(getCorsConfigurations());
    return mapping;
}

此外WebMvcConfigurationSupport类还会创建其他组件,比如HandlerAdapter,分别是:RequestMappingHandlerAdapter、HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter,还会创建HandlerExceptionResolver。

我们已RequestMappingHandlerMapping为例,获取Handler的逻辑如下,RequestMappingHandlerMapping集成了AbstractHandlerMapping,主要流程有父类定义。首先会尝试获取Handler,如果找不到会找默认的Handler。如果Handler为字符串,则需要从IOC容器中获取实例,最后再封装成Chain,主要是方便执行拦截逻辑。

//AbstractHandlerMapping#getHandler
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);
    }

    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
	

    return executionChain;
}

getHandlerInternal由子类实现,RequestMappingHandlerMapping也继承了AbstractHandlerMethodMapping,大致逻辑是会先逻辑要查找的路径,然后通过路径查找HandlerMethod然后返回。

//AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    this.mappingRegistry.acquireReadLock();
    try {
       HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
       return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
       this.mappingRegistry.releaseReadLock();
    }
}

3.2 HandlerMethod维护

在查询HandlerMethod之前,我们看下HandlerMethod是怎么构建的。在AbstractHandlerMethodMapping的子类Bean初始化的时候(实现了InitializingBean接口,会在初始化的时候调用afterPropertiesSet方法)进行初始化。

//AbstractHandlerMethodMapping#afterPropertiesSet
public void afterPropertiesSet() {
    initHandlerMethods();
}

会从ApplicationContext中获取所有bean,然后进行遍历处理。最后在执行HandlerMethod初始化,实际执行就是打印日志。

//AbstractHandlerMethodMapping#initHandlerMethods
protected void initHandlerMethods() {
    for (String beanName : getCandidateBeanNames()) {
       if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
          processCandidateBean(beanName);
       }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

会先获取beanName对应的Class,然后判断是不是Handler,isHandler是个抽象方法,在RequestMappingHandlerMapping中的实现,就是判断是否有@Controller注解或者有@RequestMapping注解。

//AbstractHandlerMethodMapping#processCandidateBean
protected void processCandidateBean(String beanName) {
    Class<?> beanType = null;
    try {
       beanType = obtainApplicationContext().getType(beanName);
    }
    catch (Throwable ex) {
    }
    if (beanType != null && isHandler(beanType)) {
       detectHandlerMethods(beanName);
    }
}
//RequestMappingHandlerMapping#isHandler
protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
          AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

detectHandlerMethods内部会通过getMappingForMethod方法获取映射关系,然后在进行注册HandlerMethod。

//AbstractHandlerMethodMapping#detectHandlerMethods
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));
}
methods.forEach((method, mapping) -> {
    Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
    registerHandlerMethod(handler, invocableMethod, mapping);
});

getMappingForMethod是一个抽象方法,RequestMappingHandlerMapping中实现为先创建RequestMappingInfo,然后返回。创建RequestMappingInfo的逻辑就是从Method中获取RequestMapping注解,然后封装成RequestMappingInfo对象。

//RequestMappingHandlerMapping#getMappingForMethod
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);
       }
       String prefix = getPathPrefix(handlerType);
       if (prefix != null) {
          info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
       }
    }
    return info;
}
//RequestMappingHandlerMapping#createRequestMappingInfo
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);
}

在registerHandlerMethod方法中,实际会调用MappingRegistry的register方法。

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

在register方法中,首先会把Handler和Method封装成HandlerMethod,然后会加入到mappingLookup中,形成Mapping ->HandlerMethod的关系。然后会计算Mapping的url,加入到urlLookup中,形成url -> Mapping的映射关系。最后还会加到到registry中,形成Mapping -> MappingRegistration的关系。

在RequestMappingHandlerMapping方法中,Mapping就是RequestMappingInfo对象。

//AbstractHandlerMethodMapping$MappingRegistry#register
public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
       HandlerMethod handlerMethod = createHandlerMethod(handler, method);
       assertUniqueMethodMapping(handlerMethod, mapping);
       this.mappingLookup.put(mapping, handlerMethod);

       List<String> directUrls = getDirectUrls(mapping);
       for (String url : directUrls) {
          this.urlLookup.add(url, mapping);
       }

       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<>(mapping, handlerMethod, directUrls, name));
    }
    finally {
       this.readWriteLock.writeLock().unlock();
    }
}

3.3 查找HandlerMethod

回到lookupHandlerMethod方法中,看一下查找逻辑。首先会从MappingRegistry中获取Mapping,也就是会到urlLookup中,根据url获取Mapping。如果有获取到则进行判断是否匹配。没有匹配的话,会尝试便利所有的Mapping。

这里循环遍历感觉性能不高,也没做缓存,不知道是否出于大多数都能通过url匹配的的情况。

//AbstractHandlerMethodMapping#lookupHandlerMethod
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
    addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
    // No choice but to go through all mappings...
    addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}

addMatchingMappings中会对获取的Mapping进行判断,是否匹配。getMatchingMapping是个抽象方法,RequestMappingInfoHandlerMapping实现会直接调用RequestMappingInfo的getMatchingCondition方法进行判断。

RequestMappingInfoHandlerMapping的Mapping就是RequestMappingInfo

//AbstractHandlerMethodMapping#addMatchingMappings
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.getMappings().get(mapping)));
       }
    }
}

在getMatchingCondition,就是会判断请求的方法、参数、header、consume、等是否匹配,如果都匹配返回一个新的RequestMappingInfo

//RequestMappingInfo#getMatchingCondition
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
    RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
    if (methods == null) {
       return null;
    }
    ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
    if (params == null) {
       return null;
    }
    HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
    if (headers == null) {
       return null;
    }
    ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
    if (consumes == null) {
       return null;
    }
    ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
    if (produces == null) {
       return null;
    }
    PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
    if (patterns == null) {
       return null;
    }
    RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
    if (custom == null) {
       return null;
    }

    return new RequestMappingInfo(this.name, patterns,
          methods, params, headers, consumes, produces, custom.getCondition());
}

如果找到了多个Mapping,则会进行排序,获取最符合的Mapping然后进行返回。

//AbstractHandlerMethodMapping#lookupHandlerMethod
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
    if (logger.isTraceEnabled()) {
       logger.trace(matches.size() + " matching mappings: " + matches);
    }
    if (CorsUtils.isPreFlightRequest(request)) {
       return PREFLIGHT_AMBIGUOUS_MATCH;
    }
    Match secondBestMatch = matches.get(1);
    if (comparator.compare(bestMatch, secondBestMatch) == 0) {
       Method m1 = bestMatch.handlerMethod.getMethod();
       Method m2 = secondBestMatch.handlerMethod.getMethod();
       String uri = request.getRequestURI();
       throw new IllegalStateException(
             "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
    }
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;

如果没有找到Mapping,则会进行处理,子类的实现(如RequestMappingInfoHandlerMapping,它是RequestMappingHandlerMapping的父类)会根据一些条件抛出异常。

//RequestMappingInfoHandlerMapping#handleNoMatch
protected HandlerMethod handleNoMatch(
       Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException {

		if (helper.hasProducesMismatch()) {
			Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
			throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
		}
}

回到getHandler逻辑中,因为获取到了Handler(实际就是HandlerMethod),会封装成HandlerExecutionChain。其实就是把adaptedInterceptors加入到HandlerExecutionChain中,便于做Handler的拦截处理。那么adaptedInterceptors是怎么维护的呢?

//AbstractHandlerMapping#getHandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
          (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

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

4. 获取HandlerAdapter

就是遍历handlerAdapters,看那个支持就返回那个HandlerAdapter。以RequestMappingHandlerAdapter为例

//DispatcherServlet#getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
       for (HandlerAdapter adapter : this.handlerAdapters) {
          if (adapter.supports(handler)) {
             return adapter;
          }
       }
    }

}

AbstractHandlerMethodAdapter是RequestMappingHandlerAdapter的父类,判断handler是HandlerMethod,且supportsInternal方法为true。我们在前面得得handler就HandlerMethod,而RequestMappingHandlerAdapter实现的supportsInternal方法默认就返回true。所以一般来说HandlerMapping和HandlerAdapter实际上就是成对出现的。RequestMappingHandlerMapping对应RequestMappingHandlerAdapter。

//AbstractHandlerMethodAdapter#supports
public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
//RequestMappingHandlerAdapter#supportsInternal
protected boolean supportsInternal(HandlerMethod handlerMethod) {
    return true;
}

5. 执行HandlerInterceptor

5.1 HandlerInterceptor维护

仅在SpringMVC的项目中,如果需要添加HandlerInterceptor,那么大致逻辑如下,会实现WebMvcConfigurer然后做@Configuration注解,该注解是实际上是@Component的派生注解,对应的类也会转换成Spring的Bean。

一般会这样定义,首先需要的HandlerInterceptor添加到Spring IOC容器中,然后通过addInterceptors逻辑注册到InterceptorRegistry里。

@Configuration
@EnableWebMvc
public class WebApplicationConfig implements WebMvcConfigurer {

   @Autowired(required = false)
   private List<HandlerInterceptor> handlerInterceptors;

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
      if (handlerInterceptors != null) {
         for (HandlerInterceptor interceptor : handlerInterceptors) {
            registry.addInterceptor(interceptor);
         }
      }
   }
}

我们知道@EnableWebMvc实际上会Import DelegatingWebMvcConfiguration,该类内部会注入WebMvcConfigurer,这个正是前面我们设置的。然后添加到WebMvcConfigurerComposite,在Spring中,xxxComposite代表了组合,就是有多个实现,然后内部实现基本上是通过遍历调用,这里的逻辑是可能有多个WebMvcConfigurer,然后进行遍历调用。DelegatingWebMvcConfiguration同时也继承了WebMvcConfigurationSupport 。

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

   private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


   @Autowired(required = false)
   public void setConfigurers(List<WebMvcConfigurer> configurers) {
      if (!CollectionUtils.isEmpty(configurers)) {
         this.configurers.addWebMvcConfigurers(configurers);
      }
   }

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

}

在前面我们知道RequestMappingHandlerMapping实际上就是WebMvcConfigurationSupport内创建的,所以HandlerMapping设置Interceptor也在RequestMappingHandlerMapping中。首先会尝试添加,添加实际就是会调用WebMvcConfigurer的addInterceptors逻辑,也就是会把自定义的HandlerInterceptor添加到InterceptorRegistry ,最后再获取所有的Interceptor。

//WebMvcConfigurationSupport#getInterceptors
protected final Object[] getInterceptors() {
   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();
}

这其实也是Spring和SpringBoot的差别,Spring提供了各种API支持不同的能力,但是需要开发者自己去组装起来。而SpringBoot默认就进行了组装,达到开箱即用的效果

5.2 执行拦截器

以前置拦截器为例,其他拦截器的方法类似,只是时机和拦截器顺序不一定(比如postHandle是从后往前执行)

实际上就是根据之前设置的HandlerInterceptor然后遍历执行preHandle方法。记录interceptorIndex的值是为了只执行了preHandle的HandlerInterceptor,去执行它们的afterCompletion方法。整体逻辑还是比较简单,但也是个很好的扩展机制,值得学习。

//HandlerExecutionChain#applyPreHandle
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
       for (int i = 0; i < interceptors.length; i++) {
          HandlerInterceptor interceptor = interceptors[i];
          if (!interceptor.preHandle(request, response, this.handler)) {
             triggerAfterCompletion(request, response, null);
             return false;
          }
          this.interceptorIndex = i;
       }
    }
    return true;
}

6. 执行HandlerAdapter

6.1 执行Handler

会用HandlerAdapter去执行Handler,返回ModelAndView。

以RequestMappingHandlerAdapter为例,实际调用handle是在父类AbstractHandlerMethodAdapter,内部会调用handleInternal方法,该方法是个抽象类。

内部主要两个逻辑,会做request的检查,如http 的method是否支持之类。然后执行invokeHandlerMethod方法,该方法是个核心的方法,逻辑比较多。

//RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request,
       HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    checkRequest(request);
    mav = invokeHandlerMethod(request, response, handlerMethod);
    return mav;
}

其中主要有4个逻辑,构建执行Handler的ServletInvocableHandlerMethod,会设置参数解析器,返回值处理器等;构建ModelAndViewContainer用于处理ModelAndView;调用Handler;返回ModelAndView。其中主要关心调用Handler

//RequestMappingHandlerAdapter#invokeHandlerMethod
protected ModelAndView invokeHandlerMethod() {
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    //构建ServletInvocableHandlerMethod
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    if(this.argumentResolvers != null) {
        invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    if(this.returnValueHandlers != null) {
        invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }
    invocableMethod.setDataBinderFactory(binderFactory);
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    //构建ModelAndViewContainer
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    modelFactory.initModel(webRequest, mavContainer, invocableMethod);
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    //调用Handler
    invocableMethod.invokeAndHandle(webRequest, mavContainer);
    //获取ModelAndView
    return getModelAndView(mavContainer, modelFactory, webRequest);
}

invokeAndHandle内部,首先会调用Handler,然后对于返回的结果进行处理。如果结果为空,或者设置了@ResponseStatus注解的reason,则会直接返回,并且标记requestHandled=true,有该标记,后面流程中获取ModelAndView会直接返回,不去获取。最后会通过HandlerMethodReturnValueHandlerComposite对返回值进行解析。

//ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
       Object... providedArgs) throws Exception {

    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    setResponseStatus(webRequest);

    if (returnValue == null) {
       if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
          disableContentCachingIfNecessary(webRequest);
          mavContainer.setRequestHandled(true);
          return;
       }
    }
    else if (StringUtils.hasText(getResponseStatusReason())) {
       mavContainer.setRequestHandled(true);
       return;
    }

    mavContainer.setRequestHandled(false);
    try {
       this.returnValueHandlers.handleReturnValue(
             returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
}

HandlerMethod实际就是作用在方法上,所以invokeForRequest会先获取Method所需的参数,最后会通过反射进行调用。

//InvocableHandlerMethod#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);
}

获取参数内部的逻辑,实际会遍历参数,然后判断参数符合哪个HandlerMethodArgumentResolver,然后进行解析。以参数中带有HttpServletRequest为例,对应的HandlerMethodArgumentResolver是ServletRequestMethodArgumentResolver,该类内部支持解析多种接口。

//ServletRequestMethodArgumentResolver#supportsParameter
public boolean supportsParameter(MethodParameter parameter) {
    Class<?> paramType = parameter.getParameterType();
    return (WebRequest.class.isAssignableFrom(paramType) ||
          ServletRequest.class.isAssignableFrom(paramType) ||
          MultipartRequest.class.isAssignableFrom(paramType) ||
          HttpSession.class.isAssignableFrom(paramType) ||
          (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
          Principal.class.isAssignableFrom(paramType) ||
          InputStream.class.isAssignableFrom(paramType) ||
          Reader.class.isAssignableFrom(paramType) ||
          HttpMethod.class == paramType ||
          Locale.class == paramType ||
          TimeZone.class == paramType ||
          ZoneId.class == paramType);
}

HttpServletRequest的参数,内部就会进行参数的解析,然后进行返回。

//ServletRequestMethodArgumentResolver#resolveArgument
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
       NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    Class<?> paramType = parameter.getParameterType();

    //...
    // ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
    if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
       return resolveNativeRequest(webRequest, paramType);
    }
}
//ServletRequestMethodArgumentResolver#resolveNativeRequest
private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
    T nativeRequest = webRequest.getNativeRequest(requiredType);
    return nativeRequest;
}

handleReturnValue内部逻辑会先获取一个HandlerMethodReturnValueHandler,然后执行handleReturnValue方法

//HandlerMethodReturnValueHandlerComposite#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

   HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
   if (handler == null) {
      throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
   }
   handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

selectHandler的逻辑是,通过supportsReturnType逻辑判断是否支持该返回值,支持旧选择该HandlerMethodReturnValueHandler。

//HandlerMethodReturnValueHandlerComposite#selectHandler
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
   boolean isAsyncValue = isAsyncReturnValue(value, returnType);
   for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
      if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
         continue;
      }
      if (handler.supportsReturnType(returnType)) {
         return handler;
      }
   }
   return null;
}

当前项目一般都是前段端分离项目,所以我们就以RequestResponseBodyMethodProcessor(实现了HandlerMethodReturnValueHandler接口)为例,看下supportsReturnType和handleReturnValue方法如何处理

supportsReturnType的判断逻辑就是,查看方法或者类中是否有ResponseBody注解。

//RequestResponseBodyMethodProcessor#supportsReturnType
public boolean supportsReturnType(MethodParameter returnType) {
   return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
         returnType.hasMethodAnnotation(ResponseBody.class));
}

handleReturnValue的逻辑,首先会把requestHandled设置为true,该逻辑前面有说过,设置为true后续将不会执行获取ModelAndView的逻辑,核心的逻辑在writeWithMessageConverters中。

//RequestResponseBodyMethodProcessor#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
      throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

   mavContainer.setRequestHandled(true);
   ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
   ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

   // Try even with null return value. ResponseBodyAdvice could get involved.
   writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

writeWithMessageConverters内主要有两大逻辑,第一:判断合适的MediaType。第二根据选择的MediaType查找合适的HttpMessageConverter,然后执行write方法。判断MediaType不展开分析,主要是根据返回值、返回类型、请求头、相应的Content-Type进行判断。HttpMessageConverter的处理主要是两个接口,符合canWrite之后会调用write。

//AbstractMessageConverterMethodProcessor#writeWithMessageConverters
converter.canWrite(valueType, selectedMediaType)
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);

下面我们就以GsonHttpMessageConverter为例,看一下这两个接口如何支持。

canWrite接口主要判断supports,该接口在子类中默认为true;canWrite主要判断MediaType是否符合,返回类型为application/json的能处理

//AbstractHttpMessageConverter#canWrite
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
    return supports(clazz) && canWrite(mediaType);
}
//AbstractGenericHttpMessageConverter#supports
protected boolean supports(Class<?> clazz) {
    return true;
}

GsonHttpMessageConverter的父类AbstractJsonHttpMessageConverter会设置supportedMediaTypes为new MediaType("application", "*+json")

//AbstractHttpMessageConverter#canWrite
boolean canWrite(@Nullable MediaType mediaType) {
    if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) {
       return true;
    }
    for (MediaType supportedMediaType : getSupportedMediaTypes()) {
       if (supportedMediaType.isCompatibleWith(mediaType)) {
          return true;
       }
    }
    return false;
}

write接口会不断的调用writeInternal,让子类进行处理,最后到GsonHttpMessageConverter时,就会用Gson.toJson把对象转成json,并且写入到Response中,这样就完成了body的返回,写完后还会调用flush。

//AbstractHttpMessageConverter#write
public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
       throws IOException, HttpMessageNotWritableException {

    final HttpHeaders headers = outputMessage.getHeaders();
    addDefaultHeaders(headers, t, contentType);

    writeInternal(t, outputMessage);
    outputMessage.getBody().flush();

}
//AbstractJsonHttpMessageConverter#writeInternal
protected final void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
       throws IOException, HttpMessageNotWritableException {

    Writer writer = getWriter(outputMessage);
    if (this.jsonPrefix != null) {
       writer.append(this.jsonPrefix);
    }
    try {
       writeInternal(object, type, writer);
    }
    catch (Exception ex) {
       throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
    }
    writer.flush();
}
//GsonHttpMessageConverter#writeInternal
protected void writeInternal(Object object, @Nullable Type type, Writer writer) throws Exception {
    if (type instanceof ParameterizedType) {
       getGson().toJson(object, type, writer);
    }
    else {
       getGson().toJson(object, writer);
    }
}

6.2 获取ModelAndView

前面有说过,会设置requestHandled为true,所以这里会返回null。

//RequestMappingHandlerAdapter#getModelAndView
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
       ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

    modelFactory.updateModel(webRequest, mavContainer);
    if (mavContainer.isRequestHandled()) {
       return null;
    }
}

回到DispatcherServlet里,会对ModelAndView进行处理,但是前面已经返回了null,所以这里也就不处理了。

//DispatcherServlet#processDispatchResult
if (mv != null && !mv.wasCleared()) {
    render(mv, request, response);
    if (errorView) {
       WebUtils.clearErrorRequestAttributes(request);
    }
}

7. 异常解析

handler执行完后,会处理结果,其中就有Exception的判断,内部会通过processHandlerException方法处理

//DispatcherServlet#processDispatchResult
if (exception != null) {
    if (exception instanceof ModelAndViewDefiningException) {
       logger.debug("ModelAndViewDefiningException encountered", exception);
       mv = ((ModelAndViewDefiningException) exception).getModelAndView();
    }
    else {
       Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
       mv = processHandlerException(request, response, handler, exception);
       errorView = (mv != null);
    }
}

异常解析遍历handlerExceptionResolvers,而默认内置的异常解析器ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver

//DispatcherServlet#processHandlerException
if (this.handlerExceptionResolvers != null) {
    for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
       exMv = resolver.resolveException(request, response, handler, ex);
       if (exMv != null) {
          break;
       }
    }
}

以DefaultHandlerExceptionResolver为例,内部会判断各种异常,然后返回对应的响应值。

//DefaultHandlerExceptionResolver#doResolveException
if (ex instanceof HttpRequestMethodNotSupportedException) {
    return handleHttpRequestMethodNotSupported(
          (HttpRequestMethodNotSupportedException) ex, request, response, handler);
}
//DefaultHandlerExceptionResolver#handleHttpRequestMethodNotSupported
protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
       HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {

    String[] supportedMethods = ex.getSupportedMethods();
    if (supportedMethods != null) {
       response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
    }
    response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
    return new ModelAndView();
}

8. 附录

8.1 Spring整合内嵌Tomcat

Tomcat的gradle依赖:org.apache.tomcat.embed:tomcat-embed-core:9.0.36、以及Spring MVC版本

启动类

public class WebApplicationStarter {

   private Tomcat tomcat;


   public static void main(String[] args) throws LifecycleException {
      WebApplicationStarter starter = new WebApplicationStarter();

      starter.start();
   }

   
   public WebApplicationStarter(){

      tomcat = new Tomcat();
      tomcat.setPort(8080);

      tomcat.getConnector();

      Context context = tomcat.addContext("", null);

      try {

         DispatcherServlet dispatcherServlet = new DispatcherServlet(createContext(context.getServletContext()));


         Wrapper wrapper = Tomcat.addServlet(context, "dispatcherServlet", dispatcherServlet);

         wrapper.setLoadOnStartup(1);

         wrapper.addMapping("/*");
         
      }catch (Exception e) {

         e.printStackTrace();
      }

   }

   public void start() throws LifecycleException {
      if (this.tomcat != null) {
         this.tomcat.start();
      }
   }


   public WebApplicationContext createContext(ServletContext context){

      AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();

      applicationContext.register(WebApplicationConfig.class);

      applicationContext.setServletContext(context);

      applicationContext.refresh();

      applicationContext.registerShutdownHook();

      return applicationContext;
   }
}

配置类

@Configuration
@ComponentScan(basePackageClasses = WebApplicationConfig.class)
@EnableWebMvc
public class WebApplicationConfig implements WebMvcConfigurer {

   @Autowired(required = false)
   private List<HandlerInterceptor> handlerInterceptors;

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
      if (handlerInterceptors != null) {
         for (HandlerInterceptor interceptor : handlerInterceptors) {
            registry.addInterceptor(interceptor);
         }
      }
   }
}

控制器

@RestController
public class TestController {


   @GetMapping("/test")
   public String test(HttpServletRequest request, @RequestParam("size") Integer size) {
      return "test";
   }
}

9. 参考资料

  • Spring源码分支5.1.x