09. Spring MVC源码解析

Posted IT BOY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了09. Spring MVC源码解析相关的知识,希望对你有一定的参考价值。

09 Spring MVC源码解析

目录

09 Spring MVC源码解析

Pt1 Spring MVC架构

Pt1.1 Servlet

Pt1.2 Servlet MVC

Pt1.3 Spring MVC

Pt2 Spring MVC九大组件

Pt2.1 HandlerMapping

Pt2.2 HandlerAdapter

Pt2.3 HandlerExceptionResolver

Pt2.4 ViewResolver

Pt2.5 RequestToViewNameTranslator

Pt2.6 LocaleResolver

Pt2.7 ThemeResolver

Pt2.8 MultipartResolver

Pt2.9 FlashMapManager

Pt3 Spring MVC核心类

Pt3.1 HandlerExecutionChain

Pt3.2 RequestMappingInfo

Pt4 Spring MVC初始化

Pt4.1 初始化入口

Pt4.2 初始化MultipartResolver

Pt4.3 初始化LocaleResolver

Pt4.4 初始化ThemeResolver

Pt4.5 初始化HandlerMapping

Pt4.6 初始化HandlerAdapter

Pt4.7 初始化HandlerExceptionResolver

Pt4.8 初始化RequestToViewNameTranslator

Pt4.9 初始化ViewResolver

Pt4.10 初始化FlashMapManager

Pt5 Spring MVC请求处理

Pt5.1 请求入口

Pt5.2 处理文件上传请求

Pt5.3 获取处理请求的HandlerMapping

Pt5.4 匹配HandlerAdapter

Pt5.5 执行Handler处理

Pt5.6 解析ModelAndView

Pt5.7 渲染View

Pt5.8 流程图

参考资料


Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的MVC架构,从而在使用Spring进行WEB开发时,可以选择使用Spring MVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts 2(一般老项目使用)等等。

 


Pt1 Spring MVC架构

Pt1.1 Servlet

在早期 Java Web 的开发中,统一把显示层、控制层、数据层的操作全部交给 JSP 或者 JavaBean 来进行处理,我们称之为 Model1:

出现的弊端:

  • JSP 和 Java Bean 之间严重耦合,Java 代码和 html 代码也耦合在了一起

  • 要求开发者不仅要掌握 Java ,还要有高超的前端水平

  • 前端和后端相互依赖,前端需要等待后端完成,后端也依赖前端完成,才能进行有效的测试

  • 代码难以复用

 

这种模式实在太老了,现在基本已经不用了,也不用在考虑了。如果还有在用的,那技术债实在是太深了,早点放弃吧。

 


Pt1.2 Servlet MVC

后来衍生了Servlet + JSP + Java Bean模型,早期的 MVC 模型(Model2)就像下图这样:

首先用户的请求会到达 Servlet,然后根据请求调用相应的 Java Bean,并把所有的显示结果交给 JSP 去完成,这样的模式我们就称为 MVC 模式。

  • M 代表 模型(Model) 模型是什么呢? 模型就是数据,就是 dao,bean

  • V 代表 视图(View) 视图是什么呢? 就是网页, JSP,用来展示模型中的数据

  • C 代表 控制器(controller) 控制器是什么? 控制器的作用就是把不同的数据(Model),显示在不同的视图(View)上,Servlet 扮演的就是这样的角色。

 

这个MVC模型在当时Struts2的大环境下,非常的火,几乎每次面试都会被问到这个问题。不过那时候对Servlet到Struts整套架构还是比较熟悉的,每次也能说得头头是道。

但是,这个时代也已经过去了。

后来,诞生了Spring MVC。

 


Pt1.3 Spring MVC

为解决持久层中一直未处理好的数据库事务的编程,又为了迎合 NoSQL 的强势崛起,Spring MVC 给出了方案:

Spring MVC优势:

  • 结构松散,几乎可以在 Spring MVC 中使用各类视图

  • 松耦合,各个模块分离

  • 与 Spring 无缝集成

 

关于Spring MVC详细的使用就不再介绍了,可以参照《Spring 实战》的讲解。我们重点关注Spring MVC核心组件初始化,已经Web请求解析和调用过程中,源码的分析。

Spring MVC核心处理流程如下:

 

①、发送请求到DispatchServlet。DispatcherServlet是Spring MVC的请求控制器,前端请求都会由DispatcherServlet接收,并分发给对应的处理组件。

这个是在web.xml中配置:

 <servlet>
      <servlet-name>dispatcherServlet</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
 <servlet-mapping>
      <servlet-name>dispatcherServlet</servlet-name>
      <url-pattern>/</url-pattern>
 </servlet-mapping>

②、HandlerMapping是负责完成Url到Controller映射的组件。从HandlerMapping中获取请求Url对应的Controller。

③和④、Controller负责处理请求信息,并返回ModelAndView对象,ModelAndView封装了结果视图的组件。

⑤、通过视图解析器解析ModelAndView,获得对应的View。

⑥、渲染获得的View信息。

⑦、将View返回给前端展现。

这是大致的流程,后续源码解析部分,我们会更深入的分析调用链路。

 


Pt2 Spring MVC九大组件

Pt2.1 HandlerMapping

HandlerMapping 是用来查找 Handler的,也就是具体的请求处理组件,可以是类也可以是一个方法。比如,用@RequestMapping 标注的就是一个 Handler,负责实际的请求处理。所以HandlerMapping的作用就是,根据请求的URL来找到具体请求处理的Handler好Intercetor。

 

如果没有显示的指明HandlerMapping类型,在DispatcherServlet.properties配置了HandlerMapping的默认实现类。

 # org/springframework/web/servlet/DispatcherServlet.properties
 org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\\
    org.springframework.web.servlet.function.support.RouterFunctionMapping

 

最常用的莫过于RequestMappingHandlerMapping,就是在Controller方法中使用@RequestMapping标记的方法,就是RequestMappingHandlerMaping,其类图如下:

下面具体分析实现逻辑。

 

Mapping种类

Spring Mapping注解有如下几种:

  • org.springframework.web.bind.annotation.@Mapping

  • org.springframework.web.bind.annotation.@RequestMapping

  • org.springframework.web.bind.annotation.@GetMapping

  • org.springframework.web.bind.annotation.@PostMapping

  • org.springframework.web.bind.annotation.@PutMapping

  • org.springframework.web.bind.annotation.@DeleteMapping

  • org.springframework.web.bind.annotation.@PatchMapping

 

注解中定义了对应的匹配信息,Spring会将匹配信息封装成RequestMappingInfo对象,然后将<RequestMappingInfo , Handler>的映射关系放到注册中心进行管理。

关于RequestMappingInfo的逻辑,可以参考RequestMappingInfo部分介绍。

 

HandlerMapping注册中心

HandlerMapping中保存所有的Url -> Handler的映射关系,是通过MappingRegistry来实现的,他是整个HandlerMapping映射管理的核心。

 // org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java:103
 // Mapping注册表:缓存映射的容器
 private final MappingRegistry mappingRegistry = new MappingRegistry();
 ​
 // Mapping注册中心,管理Mapping。
 // <T>是Mapping的类型,比如子类RequestMappingInfoHandlerMapping用的就是RequestMappingInfo,对应的就是@RequestMapping信息。
 // 所以<T>是Request请求对应的匹配信息,我们可以从指定的Request中匹配到T的信息,从而进一步获取对应的Handler信息,即MappingRegistration<T>。
 class MappingRegistry {
 ​
     // 映射1:Mapping与MappingRegistration的映射
     // Key -> Mapping
     // Value -> {@link MappingRegistration}(Mapping + HandlerMethod)
     private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
 ​
     // 映射2:Mapping名字与HandlerMethod的映射
     //  Key:Mapping 的名字
     //  Value:HandlerMethod 数组
     private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
 ​
     // 映射3:直接URL的映射
     //  Key:直接 URL(就是固定死的路径,而非多个)
     //  Value:Mapping对象
     private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
 ​
     // HandlerMethod的跨域配置
     private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
 ​
     // 读写锁
     private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 ​
     /**
          * Return all registrations.
          * @since 5.3
          */
     public Map<T, MappingRegistration<T>> getRegistrations() {
         return this.registry;
     }
 ​
     /**
          * Return matches for the given URL path. Not thread-safe.
          * @see #acquireReadLock()
          */
     @Nullable
     public List<T> getMappingsByDirectPath(String urlPath) {
         return this.pathLookup.get(urlPath);
     }
 ​
     /**
          * Return handler methods by mapping name. Thread-safe for concurrent use.
          */
     public List<HandlerMethod> getHandlerMethodsByMappingName(String mappingName) {
         return this.nameLookup.get(mappingName);
     }
 ​
     /**
          * Return CORS configuration. Thread-safe for concurrent use.
          */
     @Nullable
     public CorsConfiguration getCorsConfiguration(HandlerMethod handlerMethod) {
         HandlerMethod original = handlerMethod.getResolvedFromHandlerMethod();
         return this.corsLookup.get(original != null ? original : handlerMethod);
     }
 ​
     /**
          * Acquire the read lock when using getMappings and getMappingsByUrl.
          */
     public void acquireReadLock() {
         this.readWriteLock.readLock().lock();
     }
 ​
     /**
          * Release the read lock after using getMappings and getMappingsByUrl.
          */
     public void releaseReadLock() {
         this.readWriteLock.readLock().unlock();
     }
 ​
     // 把Url和Handler的Mapping关系进行注册
     public void register(T mapping, Object handler, Method method) {
         this.readWriteLock.writeLock().lock();
         try {
             // 封装HandlerMethod对象
             HandlerMethod handlerMethod = createHandlerMethod(handler, method);
             validateMethodMapping(handlerMethod, mapping);
 ​
             // 添加直接URL的映射关系
             Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
             for (String path : directPaths) {
                 this.pathLookup.add(path, mapping);
             }
 ​
             // 添加Mapping名称和HandlerMethod映射关系
             String name = null;
             if (getNamingStrategy() != null) {
                 name = getNamingStrategy().getName(handlerMethod, mapping);
                 addMappingName(name, handlerMethod);
             }
 ​
             CorsConfiguration config = initCorsConfiguration(handler, method, mapping);
             if (config != null) {
                 config.validateAllowCredentials();
                 this.corsLookup.put(handlerMethod, config);
             }
 ​
             // 将当前HandlerMapping信息添加到注册中心
             this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name));
         }
         finally {
             this.readWriteLock.writeLock().unlock();
         }
     }
 ​
     // 校验HandlerMapping
     private void validateMethodMapping(HandlerMethod handlerMethod, T mapping) {
         MappingRegistration<T> registration = this.registry.get(mapping);
         HandlerMethod existingHandlerMethod = (registration != null ? registration.getHandlerMethod() : null);
         if (existingHandlerMethod != null && !existingHandlerMethod.equals(handlerMethod)) {
             throw new IllegalStateException(
                 "Ambiguous mapping. Cannot map '" + handlerMethod.getBean() + "' method \\n" +
                 handlerMethod + "\\nto " + mapping + ": There is already '" +
                 existingHandlerMethod.getBean() + "' bean method\\n" + existingHandlerMethod + " mapped.");
         }
     }
 ​
     // 新增【Mapping 的名字与 HandlerMethod 的映射】
     private void addMappingName(String name, HandlerMethod handlerMethod) {
         List<HandlerMethod> oldList = this.nameLookup.get(name);
         if (oldList == null) {
             oldList = Collections.emptyList();
         }
 ​
         for (HandlerMethod current : oldList) {
             if (handlerMethod.equals(current)) {
                 return;
             }
         }
 ​
         List<HandlerMethod> newList = new ArrayList<>(oldList.size() + 1);
         newList.addAll(oldList);
         newList.add(handlerMethod);
         this.nameLookup.put(name, newList);
     }
 ​
     // 卸载指定HandlerMapping
     public void unregister(T mapping) {
         this.readWriteLock.writeLock().lock();
         try {
             MappingRegistration<T> registration = this.registry.remove(mapping);
             if (registration == null) {
                 return;
             }
 ​
             for (String path : registration.getDirectPaths()) {
                 List<T> mappings = this.pathLookup.get(path);
                 if (mappings != null) {
                     mappings.remove(registration.getMapping());
                     if (mappings.isEmpty()) {
                         this.pathLookup.remove(path);
                     }
                 }
             }
 ​
             removeMappingName(registration);
 ​
             this.corsLookup.remove(registration.getHandlerMethod());
         }
         finally {
             this.readWriteLock.writeLock().unlock();
         }
     }
 ​
     // 卸载名称映射
     private void removeMappingName(MappingRegistration<T> definition) {
         String name = definition.getMappingName();
         if (name == null) {
             return;
         }
         HandlerMethod handlerMethod = definition.getHandlerMethod();
         List<HandlerMethod> oldList = this.nameLookup.get(name);
         if (oldList == null) {
             return;
         }
         if (oldList.size() <= 1) {
             this.nameLookup.remove(name);
             return;
         }
         List<HandlerMethod> newList = new ArrayList<>(oldList.size() - 1);
         for (HandlerMethod current : oldList) {
             if (!current.equals(handlerMethod)) {
                 newList.add(current);
             }
         }
         this.nameLookup.put(name, newList);
     }
 }

注册表存储的是MappingRegistration对象,里面存储了HandlerMapping所有信息。

 // MappingRegistration存储了所有的HandlerMapping信息,包括定义的name、path,和匹配的HandlerMethod
 static class MappingRegistration<T> {
 ​
     // 映射信息对象
     private final T mapping;
 ​
     // 对应的HandlerMethod
     private final HandlerMethod handlerMethod;
 ​
     // 绝对路径
     private final Set<String> directPaths;
 ​
     // mapping名称
     @Nullable
     private final String mappingName;
 ​
     public MappingRegistration(T mapping, HandlerMethod handlerMethod,
                                @Nullable Set<String> directPaths, @Nullable String mappingName) {
 ​
         Assert.notNull(mapping, "Mapping must not be null");
         Assert.notNull(handlerMethod, "HandlerMethod must not be null");
         this.mapping = mapping;
         this.handlerMethod = handlerMethod;
         this.directPaths = (directPaths != null ? directPaths : Collections.emptySet());
         this.mappingName = mappingName;
     }
 ​
     public T getMapping() {
         return this.mapping;
     }
 ​
     public HandlerMethod getHandlerMethod() {
         return this.handlerMethod;
     }
 ​
     public Set<String> getDirectPaths() {
         return this.directPaths;
     }
 ​
     @Nullable
     public String getMappingName() {
         return this.mappingName;
     }
 }
 ​
 // Match封装了Mapping信息和对应的核心处理器HandlerMethod
 private class Match {
 ​
     private final T mapping;
 ​
     private final HandlerMethod handlerMethod;
 ​
     public Match(T mapping, HandlerMethod handlerMethod) {
         this.mapping = mapping;
         this.handlerMethod = handlerMethod;
     }
 ​
     @Override
     public String toString() {
         return this.mapping.toString();
     }
 }
 ​
 // 比较两个Match
 private class MatchComparator implements Comparator<Match> {
 ​
     private final Comparator<T> comparator;
 ​
     public MatchComparator(Comparator<T> comparator) {
         this.comparator = comparator;
     }
 ​
     @Override
     public int compare(Match match1, Match match2) {
         return this.comparator.compare(match1.mapping, match2.mapping);
     }
 }
 ​
 // 空Handler
 private static class EmptyHandler {
 ​
     @SuppressWarnings("unused")
     public void handle() {
         throw new UnsupportedOperationException("Not implemented");
     }
 }

上面介绍的就是缓存HandlerMapping的容器结构,那容器中数据是何时完成初始化的呢?

 

HandlerMapping初始化

RequestMappingHandlerMapping继承了InitializingBean接口,实现了afterPropertiesSet(),对象创建完成后将调用afterPropertiesSet()执行初始化操作,HandlerMapping关系的创建就是在这时候完成的。

 // org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet
 // InitializingBean接口实现类:对象创建后将调用afterPropertiesSet()完成初始化操作。
 @Override
 @SuppressWarnings("deprecation")
 public void afterPropertiesSet() {
     // RequestMappingInfo基本配置
     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()完成核心逻辑的处理:

 // org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
 ​
 // HandlerMapping对象初始化逻辑
 @Override
 public void afterPropertiesSet() {
     initHandlerMethods();
 }
 ​
 // 遍历IoC容器中的Bean,获取HandlerMethod对象并完成HandlerMapping注册。
 protected void initHandlerMethods() {
     // 遍历所有BeanName
     for (String beanName : getCandidateBeanNames()) {
         if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
             processCandidateBean(beanName);
         }
     }
     handlerMethodsInitialized(getHandlerMethods());
 }
 ​
 // 获取需要处理的所有BeanName
 protected String[] getCandidateBeanNames() {
     return (this.detectHandlerMethodsInAncestorContexts ?
             // 返回当前上下文和父上下文中所有BeanName
             BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
             // 返回当前上下文所有BeanName(Object.class)
             obtainApplicationContext().getBeanNamesForType(Object.class));
 }
 ​
 // 处理指定的BeanName
 protected void processCandidateBean(String beanName) {
     // 获取BeanName对应的Class类型
     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);
         }
     }
 ​
     // 如果Bean被@Controller或者@RequestMapping注解,则处理Bean中的handler定义
     if (beanType != null && isHandler(beanType)) {
         detectHandlerMethods(beanName);
     }
 }
 ​
 // 从Bean中找到HandlerMethod,并完成HandlerMapping注册
 protected void detectHandlerMethods(Object handler) {
     // 获取Bean的Class类型
     Class<?> handlerType = (handler instanceof String ?
                             obtainApplicationContext().getType((String) handler) : handler.getClass());
 ​
     if (handlerType != null) {
         // 获得真实的 Class 对象,因为 `handlerType` 可能是代理类
         Class<?> userType = ClassUtils.getUserClass(handlerType);
 ​
         // 获得匹配的方法和对应的 Mapping 对象
         Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                                                                   (MethodIntrospector.MetadataLookup<T>) method -> {
                                                                       try {
                                                                           // 创建该方法对应的 Mapping 对象,例如根据 @RequestMapping 注解创建 RequestMappingInfo 对象
                                                                           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));
         }
 ​
         // 遍历方法,逐个注册 HandlerMethod
         methods.forEach((method, mapping) -> {
             Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
             registerHandlerMethod(handler, invocableMethod, mapping);
         });
     }
 }
 ​
 private String formatMappings(Class<?> userType, Map<Method, T> methods) {
     String formattedType = Arrays.stream(ClassUtils.getPackageName(userType).split("\\\\."))
         .map(p -> p.substring(0, 1))
         .collect(Collectors.joining(".", "", "." + userType.getSimpleName()));
     Function<Method, String> methodFormatter = method -> Arrays.stream(method.getParameterTypes())
         .map(Class::getSimpleName)
         .collect(Collectors.joining(",", "(", ")"));
     return methods.entrySet().stream()
         .map(e -> {
             Method method = e.getKey();
             return e.getValue() + ": " + method.getName() + methodFormatter.apply(method);
         })
         .collect(Collectors.joining("\\n\\t", "\\n\\t" + formattedType + ":" + "\\n\\t", ""));
 }
 ​
 // 注册HandlerMapping
 protected void registerHandlerMethod(Object handler, Method method, T mapping) {
     this.mappingRegistry.register(mapping, handler, method);
 }
 ​
 // 封装HandlerMethod对象
 protected HandlerMethod createHandlerMethod(Object handler, Method method) {
     if (handler instanceof String) {
         return new HandlerMethod((String) handler,
                                  obtainApplicationContext().getAutowireCapableBeanFactory(), method);
     }
     return new HandlerMethod(handler, method);
 }
 ​
 /**
      * Extract and return the CORS configuration for the mapping.
      */
 @Nullable
 protected CorsConfiguration initCorsConfiguration(Object handler, Method method, T mapping) {
     return null;
 }
 ​
 // 日志处理
 protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
     // Total includes detected mappings + explicit registrations via registerMapping
     int total = handlerMethods.size();
     if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
         logger.debug(total + " mappings in " + formatMappingName());
     }
 }

到此,已经完成HandlerMapping映射关系的初始化,缓存了所有Request Url的HandlerMapping。

接下来,看看HandlerMapping的读取逻辑。

 

HandlerMapping的读取

HandlerMapping接口中只有一个方法,就是用来读取HandlerMapping数据的。

 public interface HandlerMapping {
 ​
    // 返回一个包含handler Object和所有拦截器的HandlerExecutionChain
    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
 }

 

抽象类AbstractHandlerMapping包含了具体实现:

 // org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
 @Override
 @Nullable
 // 根据request请求获取HandlerExecutionChain处理程序执行器链,其中包含了Handler和所有拦截器。
 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 ​
    // 1、获取Handler object即HandlerMethod对象。
    // 具体处理逻辑由子类实现:根据请求URL,遍历Controller找到对应的Method
    Object handler = getHandlerInternal(request);
    if (handler == null) {
       // 如果没有找到匹配的HandlerMethod对象,那么就获取默认的handler
       handler = getDefaultHandler();
    }
    if (handler == null) {
       return null;
    }
 ​
    // 2、Controller还未实例化,handler获取的是BeanName而不是实例化对象
    if (handler instanceof String) {
       String handlerName = (String) handler;
       // IoC + DI获取handler实例化对象
       handler = obtainApplicationContext().getBean(handlerName);
    }
 ​
    // 3、将Handler Object和所有拦截器封装为HandlerExecutionChain
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
 ​
    if (logger.isTraceEnabled()) {
       logger.trace("Mapped to " + handler);
    }
    else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
       logger.debug("Mapped to " + executionChain.getHandler());
    }
 ​
    // 4、如果设置了跨域,配置跨域信息并放入handler中
    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;
 }

 

查找逻辑。

// org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
// 通过request请求获取对应的HandlerMethod类
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {

   // 从Request中获取请求的Url路径,比如@RequestMapping配置的路径,该路径用于寻找匹配的Handler。
   // Eg.请求地址为 http://localhost:8080/merconsole/login,则lookupPath=merconsole/login
   String lookupPath = initLookupPath(request);

   // 读锁
   this.mappingRegistry.acquireReadLock();
   try {
      // 遍历Controller上的方法,找到Url匹配的处理方法。
      // 查找Url对应的HandlerMethod,即Controller中的对应的处理方法。
      HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
      return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
   }
   finally {
      this.mappingRegistry.releaseReadLock();
   }
}

// 通过URL获取HandlerMethod对象
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
   List<Match> matches = new ArrayList<>();

   // 通过URL获取RequestMappingInfo集合
   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)) {
            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;
   }
   else {
      return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
   }
}

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).getHandlerMethod()));
      }
   }
}
 
// org.springframework.web.servlet.handler.AbstractHandlerMapping#initLookupPath
// 根据Request获取Handler object即HandlerMethod对象。
// 具体逻辑由子类实现
@Nullable
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

// 从Request中获取请求的Url路径,比如@RequestMapping配置的路径,该路径用于寻找匹配的Handler。
protected String initLookupPath(HttpServletRequest request) {
    if (usesPathPatterns()) {
        request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE);
        RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request);
        String lookupPath = requestPath.pathWithinApplication().value();
        return UrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath);
    }
    else {
        return getUrlPathHelper().resolveAndCacheLookupPath(request);
    }
}

 

封装HandlerExecutionChain

// 通过Handler和Interceptor组装成HandlerExecutionChain
// 拦截器是根据url匹配的,因为拦截器可以针对url配置
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   // 获取HandlerExecutionChain对象
   HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
         (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

   // 根据Request Url找到匹配的拦截器链。
   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;
}

 


Pt2.2 HandlerAdapter

HandlerAdapter是一个适配器,因为在Spring MVC 中 Handler可以是任意形式的,只有能完成请求处理就可以。但是请求最终交给Servlet进行处理的时候,Servlet的方法结构都是固定的,是doService(HttpServletRequest request, HttpServletResponse response)这种形式,他只能处理 request 和 response。HandlerAdapter就是将任意形式的Handler通过使用适配器,可以“转换”成固定形式,然后交给Servlet来处理。

 

组件配置

可以显示指定要配置的HanlderAdapter类型:

<bean id="HttpRequestHandlerAdapter" class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter">
</bean>

如果没有显式的指定类型,DispatcherServlet.properties中配置了默认的类型:

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\\
   org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\\
   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\\
   org.springframework.web.servlet.function.support.HandlerFunctionAdapter

其中,以SimpleControllerHandlerAdapter为例,我们分析具体的源码实现,先来看下类图,非常简单。

 

源码分析

HandlerAdapter是顶层类:

// HandlerAdapter是一个适配器接口类,用来解决SpringMVC和Servlet接口形式不兼容的处理。
public interface HandlerAdapter {

	// 判定是否支持传入的handler
	boolean supports(Object handler);

	// 使用给定的handler来处理当前request请求,得到ModelAndView结果。
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	// 返回handler最后修改的时间
	long getLastModified(HttpServletRequest request, Object handler);
}

 

SimpleControllerHandlerAdapter也非常简单,核心方法就是通过指定的Handler调用完成Request处理,具体的Handler处理就是我们代码中的Controller方法。

// SimpleControllerHandlerAdapter是基于 Controller 接口的 HandlerAdapter 实现类。
public class SimpleControllerHandlerAdapter implements HandlerAdapter {

   // 支持Controllerr类型的实现类
   @Override
   public boolean supports(Object handler) {
      return (handler instanceof Controller);
   }

   // 通过给定的Handler处理
   @Override
   @Nullable
   public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {

      return ((Controller) handler).handleRequest(request, response);
   }

   // 获取最后修改时间
   @Override
   public long getLastModified(HttpServletRequest request, Object handler) {
      if (handler instanceof LastModified) {
         return ((LastModified) handler).getLastModified(request);
      }
      return -1L;
   }
}

 


Pt2.3 HandlerExceptionResolver

HandlerExceptionResolver是用来处理 Handler 产生异常情况的组件。此组件的作用是根据异常设置ModelAndView,之后交给渲染方法进行渲染,再以页面的方式呈现。

不过,HandlerExceptionResolver只用于解析对请求的处理阶段产生的异常,渲染阶段的异常不归他管。

 

组件配置

可以在配置文件中显示指定组件的实现:

<bean class="org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver"/> 

 

如果没有显式的指定,在DispatcherServlet.properties中定义了默认的配置:

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

我们以DefaultHandlerExceptionResolver为例,分析源码实现。先来看下类图:

 

源码分析

HandlerExceptionResolver是底层接口,定义了异常的处理操作。

// 解决Handler在处理过程中产生的异常信息。
public interface HandlerExceptionResolver {

	// 解析Handler在处理Request过程中的Exception,尝试得到一个指定的错误页面。
	@Nullable
	ModelAndView resolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

 

HandlerExceptionResolver抽象类,定义了处理异常的步骤,是所有实现类的基类。

// HandlerExceptionResolver抽象类,定义了处理异常的步骤,是所有实现类的基类。
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {

	private static final String HEADER_CACHE_CONTROL = "Cache-Control";
	protected final Log logger = LogFactory.getLog(getClass());
	@Nullable
	private Log warnLogger;

	// 优先级,默认最低
	private int order = Ordered.LOWEST_PRECEDENCE;

	// 匹配的处理器对象的集合
	@Nullable
	private Set<?> mappedHandlers;

	// 匹配的处理器类型的数组
	@Nullable
	private Class<?>[] mappedHandlerClasses;

	// 阻止响应被缓存
	private boolean preventResponseCaching = false;

	// 设置处理优先级
	public void setOrder(int order) {
		this.order = order;
	}
	@Override
	public int getOrder() {
		return this.order;
	}

	// 设置匹配的Handler对象
	public void setMappedHandlers(Set<?> mappedHandlers) {
		this.mappedHandlers = mappedHandlers;
	}

	// 设置匹配的Handler类型
	public void setMappedHandlerClasses(Class<?>... mappedHandlerClasses) {
		this.mappedHandlerClasses = mappedHandlerClasses;
	}

	// 日志处理
	public void setWarnLogCategory(String loggerName) {
		this.warnLogger = (StringUtils.hasLength(loggerName) ? LogFactory.getLog(loggerName) : null);
	}

	// 设置响应缓存的模式
	public void setPreventResponseCaching(boolean preventResponseCaching) {
		this.preventResponseCaching = preventResponseCaching;
	}

	// 判断当前Resolver是否直接解决传入的异常。如果支持处理,再通过指定的实现类完成异常处理。
	@Override
	@Nullable
	public ModelAndView resolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

		if (shouldApplyTo(request, handler)) {
			prepareResponse(ex, response);
			// 委派给子类来处理异常
			ModelAndView result = doResolveException(request, response, handler, ex);
			// 日志处理
			if (result != null) {
				// Print debug message when warn logger is not enabled.
				if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
					logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
				}
				// Explicitly configured warn logger in logException method.
				logException(ex, request);
			}
			return result;
		}
		else {
			return null;
		}
	}

	// 判断mappedHandlers或者mappedHandlerClasses是否支持处理当前handler类型产生的异常
	protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
		// 判断mappedHandlers或者mappedHandlerClasses是否支持处理当前handler类型产生的异常
		if (handler != null) {
			if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
				return true;
			}
			if (this.mappedHandlerClasses != null) {
				for (Class<?> handlerClass : this.mappedHandlerClasses) {
					if (handlerClass.isInstance(handler)) {
						return true;
					}
				}
			}
		}

		return !hasHandlerMappings();
	}

	// 判断是否有mappedHandlers或者mappedHandlerClasses定义
	protected boolean hasHandlerMappings() {
		return (this.mappedHandlers != null || this.mappedHandlerClasses != null);
	}

	// 异常信息处理
	protected void logException(Exception ex, HttpServletRequest request) {
		if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
			this.warnLogger.warn(buildLogMessage(ex, request));
		}
	}
	protected String buildLogMessage(Exception ex, HttpServletRequest request) {
		return "Resolved [" + ex + "]";
	}

	// 设置response的是否缓存
	protected void prepareResponse(Exception ex, HttpServletResponse response) {
		if (this.preventResponseCaching) {
			preventCaching(response);
		}
	}

	// 设置响应头信息,不缓存
	protected void preventCaching(HttpServletResponse response) {
		response.addHeader(HEADER_CACHE_CONTROL, "no-store");
	}


	// 处理异常,核心逻辑由具体子类实现
	@Nullable
	protected abstract ModelAndView doResolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

 

DefaultHandlerExceptionResolver继承 AbstractHandlerExceptionResolver 抽象类,默认 HandlerExceptionResolver 实现类,主要功能是针对各种异常,设置错误响应码

// DefaultHandlerExceptionResolver继承 AbstractHandlerExceptionResolver 抽象类,默认 HandlerExceptionResolver 实现类,
// 针对各种异常,设置错误响应码
public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {

	/**
	 * Log category to use when no mapped handler is found for a request.
	 * @see #pageNotFoundLogger
	 */
	public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";

	/**
	 * Additional logger to use when no mapped handler is found for a request.
	 * @see #PAGE_NOT_FOUND_LOG_CATEGORY
	 */
	protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);


	/**
	 * Sets the {@linkplain #setOrder(int) order} to {@link #LOWEST_PRECEDENCE}.
	 */
	public DefaultHandlerExceptionResolver() {
		setOrder(Ordered.LOWEST_PRECEDENCE);
		setWarnLogCategory(getClass().getName());
	}

	// 处理异常信息
	@Override
	@Nullable
	protected ModelAndView doResolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

		// 针对不同的异常类型进行处理,设置对应的HTTP返回码。
		try {
			if (ex instanceof HttpRequestMethodNotSupportedException) {
				return handleHttpRequestMethodNotSupported(
						(HttpRequestMethodNotSupportedException) ex, request, response, handler);
			}
			else if (ex instanceof HttpMediaTypeNotSupportedException) {
				return handleHttpMediaTypeNotSupported(
						(HttpMediaTypeNotSupportedException) ex, request, response, handler);
			}
			else if (ex instanceof HttpMediaTypeNotAcceptableException) {
				return handleHttpMediaTypeNotAcceptable(
						(HttpMediaTypeNotAcceptableException) ex, request, response, handler);
			}
			else if (ex instanceof MissingPathVariableException) {
				return handleMissingPathVariable(
						(MissingPathVariableException) ex, request, response, handler);
			}
			else if (ex instanceof MissingServletRequestParameterException) {
				return handleMissingServletRequestParameter(
						(MissingServletRequestParameterException) ex, request, response, handler);
			}
			else if (ex instanceof ServletRequestBindingException) {
				return handleServletRequestBindingException(
						(ServletRequestBindingException) ex, request, response, handler);
			}
			else if (ex instanceof ConversionNotSupportedException) {
				return handleConversionNotSupported(
						(ConversionNotSupportedException) ex, request, response, handler);
			}
			else if (ex instanceof TypeMismatchException) {
				return handleTypeMismatch(
						(TypeMismatchException) ex, request, response, handler);
			}
			else if (ex instanceof HttpMessageNotReadableException) {
				return handleHttpMessageNotReadable(
						(HttpMessageNotReadableException) ex, request, response, handler);
			}
			else if (ex instanceof HttpMessageNotWritableException) {
				return handleHttpMessageNotWritable(
						(HttpMessageNotWritableException) ex, request, response, handler);
			}
			else if (ex instanceof MethodArgumentNotValidException) {
				return handleMethodArgumentNotValidException(
						(MethodArgumentNotValidException) ex, request, response, handler);
			}
			else if (ex instanceof MissingServletRequestPartException) {
				return handleMissingServletRequestPartException(
						(MissingServletRequestPartException) ex, request, response, handler);
			}
			else if (ex instanceof BindException) {
				return handleBindException((BindException) ex, request, response, handler);
			}
			else if (ex instanceof NoHandlerFoundException) {
				return handleNoHandlerFoundException(
						(NoHandlerFoundException) ex, request, response, handler);
			}
			else if (ex instanceof AsyncRequestTimeoutException) {
				return handleAsyncRequestTimeoutException(
						(AsyncRequestTimeoutException) ex, request, response, handler);
			}
		}
		catch (Exception handlerEx) {
			if (logger.isWarnEnabled()) {
				logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
			}
		}
		return null;
	}

	/**
	 * Handle the case where no request handler method was found for the particular HTTP request method.
	 * <p>The default implementation logs a warning, sends an HTTP 405 error, sets the "Allow" header,
	 * and returns an empty {@code ModelAndView}. Alternatively, a fallback view could be chosen,
	 * or the HttpRequestMethodNotSupportedException could be rethrown as-is.
	 * @param ex the HttpRequestMethodNotSupportedException to be handled
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler the executed handler, or {@code null} if none chosen
	 * at the time of the exception (for example, if multipart resolution failed)
	 * @return an empty ModelAndView indicating the exception was handled
	 * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
	 */
	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();
	}

	/**
	 * Handle the case where no {@linkplain org.springframework.http.converter.HttpMessageConverter message converters}
	 * were found for the PUT or POSTed content.
	 * <p>The default implementation sends an HTTP 415 error, sets the "Accept" header,
	 * and returns an empty {@code ModelAndView}. Alternatively, a fallback view could
	 * be chosen, or the HttpMediaTypeNotSupportedException could be rethrown as-is.
	 * @param ex the HttpMediaTypeNotSupportedException to be handled
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler the executed handler
	 * @return an empty ModelAndView indicating the exception was handled
	 * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
	 */
	protected ModelAndView handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex,
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {

		response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
		List<MediaType> mediaTypes = ex.getSupportedMediaTypes();
		if (!CollectionUtils.isEmpty(mediaTypes)) {
			response.setHeader("Accept", MediaType.toString(mediaTypes));
		}
		return new ModelAndView();
	}

	/**
	 * Handle the case where no {@linkplain org.springframework.http.converter.HttpMessageConverter message converters}
	 * were found that were acceptable for the client (expressed via the {@code Accept} header.
	 * <p>The default implementation sends an HTTP 406 error and returns an empty {@code ModelAndView}.
	 * Alternatively, a fallback view could be chosen, or the HttpMediaTypeNotAcceptableException
	 * could be rethrown as-is.
	 * @param ex the HttpMediaTypeNotAcceptableException to be handled
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler the executed handler
	 * @return an empty ModelAndView indicating the exception was handled
	 * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
	 */
	protected ModelAndView handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {

		response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
		return new ModelAndView();
	}

	/**
	 * Handle the case when a declared path variable does not match any extracted URI variable.
	 * <p>The default implementation sends an HTTP 500 error, and returns an empty {@code ModelAndView}.
	 * Alternatively, a fallback view could be chosen, or the MissingPathVariableException
	 * could be rethrown as-is.
	 * @param ex the MissingPathVariableException to be handled
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler the executed handler
	 * @return an empty ModelAndView indicating the exception was handled
	 * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
	 * @since 4.2
	 */
	protected ModelAndView handleMissingPathVariable(MissingPathVariableException ex,
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {

		response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage());
		return new ModelAndView();
	}

	/**
	 * Handle the case when a required parameter is missing.
	 * <p>The default implementation sends an HTTP 400 error, and returns an empty {@code ModelAndView}.
	 * Alternatively, a fallback view could be chosen, or the MissingServletRequestParameterException
	 * could be rethrown as-is.
	 * @param ex the MissingServletRequestParameterException to be handled
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler the executed handler
	 * @return an empty ModelAndView indicating the exception was handled
	 * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
	 */
	protected ModelAndView handleMissingServletRequestParameter(MissingServletRequestParameterEx

以上是关于09. Spring MVC源码解析的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC源码分析--视图解析过程

Spring MVC注解Controller源码流程解析--映射建立

spring mvc源码解析

Spring MVC 初始化源码—<mvc:annotation-driven >配置标签的源码解析

源码篇Spring MVC多种请求入参处理方式都在这了(@RequestParam@PathVariable@RequestBodyMapJavaModelRequest基础类型)

Spring6源码・MVC请求处理流程源码解析