「推荐收藏!」【Spring源码探究】(一)MVC容器初始化🏅彻底让你明白运行原理和源码流程

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 「推荐收藏!」【Spring源码探究】(一)MVC容器初始化🏅彻底让你明白运行原理和源码流程

前提背景


SpringMVC作为REST服务的一个表现层框架。对SpringMVC的设计思想和源码实现的剖析,从抽象意义上的设计层面和实现意义上的代码层面两个方面,逐一揭开SpringMVC神秘的面纱。


特定的适用领域,框架的设计和实现,必定是为了应付该领域内许多通用的、基础的工作而生。

SpringMVC作为一个表现层框架,并给出自己的回答:


  • URL到框架的映射。
  • http请求参数绑定
  • http响应的生成和输出



组成一个完整的web请求流程,一个框架,首要的是要先领会它的设计思想。从抽象、从全局上来审视这个框架。其中最具有参考价值的,就是这个框架所定义的核心接口。核心接口定义了框架的骨架,也在最抽象的意义上表达了框架的设计思想。


以一个web请求流程为载体,依次介绍SpringMVC的核心接口和类。


Http请求工具中,输入:www.xxxx.com/aaa/bbb.ccc…

看一下出现在你面前的核心接口,它是在org.springframework.web.servlet包中定义


HandlerMapping接口:

package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
public interface HandlerMapping {
    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
复制代码


从框架的设计者口中得到最准确的关于这个类或者接口的设计说明。类中定义的几个常量,我们先不去管它。关键在于这个接口中唯一的方法:

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
复制代码


它有一个类型为HttpServletRequest的参数,throws Exception的声明表示它不处理任何类型的异常,HandlerExecutionChain是它的返回类型。




DispatcherServlet处理机制


DispatcherServlet的处理流程,当DispatcherServlet接收到web请求后,由标准Servlet类处理方法doGet或者doPost,经过几次转发后,最终注册在DispatcherServlet类中的HandlerMapping实现类组成的一个List会在一个循环中被遍历。


以该web请求的HttpServletRequest对象为参数,依次调用其getHandler方法,第一个不为null的调用结果,将被返回。DispatcherServlet类中的这个遍历方法不长。

/**
 * Return the HandlerExecutionChain for this request.
 * <p>Tries all handler mappings in order.
 * @param request current HTTP request
 * @return the HandlerExecutionChain, or <code>null</code> if no handler could be found
 */
 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    for (HandlerMapping hm : this.handlerMappings) {
      if (logger.isTraceEnabled()) {
        logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
      }
      HandlerExecutionChain handler = hm.getHandler(request);
      if (handler != null) {
        return handler;
      }
    }
    return null;
}
复制代码


一个web请求经过处理后,会得到一个HandlerExecutionChain对象,需要留意的是,HandlerMapping接口的getHandler方法参数是HttpServletRequest,HandlerMapping的实现类可以利用HttpServletRequest中的所有信息来做出这个HandlerExecutionChain对象的生成”决策“。这包括,请求头、url路径、cookie、session、参数等等一切你从一个web请求中可以得到的任何东西(最常用的是url路径)。




SpringMVC的第一个扩展点,就出现在这里。可以编写任意HandlerMapping实现类,依据任何策略来决定一个web请求到HandlerExecutionChain对象的生成,从第一个核心接口的声明开始,SpringMVC就把自己的灵活性和野心暴露无疑:哥玩的就是”Open-Closed“。



HandlerExecutionChain这个类,就是我们下一个要了解的核心类,这个对象是一个执行链的封装。

HandlerExecutionChain类的代码不长,它定义在org.springframework.web.servlet包中,为了更直观的理解,先上代码。


package org.springframework.web.servlet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.util.CollectionUtils;
public class HandlerExecutionChain {
  private final Object handler;
  private HandlerInterceptor[] interceptors;
  private List<HandlerInterceptor> interceptorList;
  public HandlerExecutionChain(Object handler) {
    this(handler, null);
  }
  public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) {
    if (handler instanceof HandlerExecutionChain) {
      HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
      this.handler = originalChain.getHandler();
      this.interceptorList = new ArrayList<HandlerInterceptor>();
      CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
      CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
    }
    else {
      this.handler = handler;
      this.interceptors = interceptors;
    }
  }
  public Object getHandler() {
    return this.handler;
  }
  public void addInterceptor(HandlerInterceptor interceptor) {
    initInterceptorList();
    this.interceptorList.add(interceptor);
  }
  public void addInterceptors(HandlerInterceptor[] interceptors) {
    if (interceptors != null) {
      initInterceptorList();
      this.interceptorList.addAll(Arrays.asList(interceptors));
    }
  }
  private void initInterceptorList() {
    if (this.interceptorList == null) {
      this.interceptorList = new ArrayList<HandlerInterceptor>();
    }
    if (this.interceptors != null) {
      this.interceptorList.addAll(Arrays.asList(this.interceptors));
      this.interceptors = null;
    }
  }
  public HandlerInterceptor[] getInterceptors() {
    if (this.interceptors == null && this.interceptorList != null) {
      this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
    }
    return this.interceptors;
  }
  @Override
  public String toString() {
    if (this.handler == null) {
      return "HandlerExecutionChain with no handler";
    }
    StringBuilder sb = new StringBuilder();
    sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]");
    if (!CollectionUtils.isEmpty(this.interceptorList)) {
      sb.append(" and ").append(this.interceptorList.size()).append(" interceptor");
      if (this.interceptorList.size() > 1) {
        sb.append("s");
      }
    }
    return sb.toString();
  }
}
复制代码



Handler类的基本接口
private final Object handler;
private HandlerInterceptor[] interceptors;
复制代码



不出我们所料,一个实质执行对象,还有一堆拦截器。SpringMVC没有避嫌,还是采用了这种封装。


得到HandlerExecutionChain这个执行链(execution chain)之后,下一步的处理将围绕其展开。


HandlerInterceptor也是SpringMVC的核心接口,定义如下:

package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface HandlerInterceptor {
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;
}
复制代码
  1. HandlerExecutionChain整个执行脉络也就清楚了:在真正调用其handler对象前,HandlerInterceptor接口实现类组成的数组将会被遍历,其preHandle方法会被依次调用,然后真正的handler对象将被调用。


  1. handler对象被调用后,就生成了需要的响应数据,在将处理结果写到HttpServletResponse对象之前(SpringMVC称为渲染视图),其postHandle方法会被依次调用。视图渲染完成后,最后afterCompletion方法会被依次调用,整个web请求的处理过程就结束了。


  1. 在一个处理对象执行之前,之后利用拦截器做文章,这已经成为一种经典的框架设计套路。那么SpringMVC的拦截器具体做些什么呢?


HandlerInterceptor,是SpringMVC的第二个扩展点的暴露,通过自定义拦截器,我们可以在一个请求被真正处理之前、请求被处理但还没输出到响应中、请求已经被输出到响应中之后这三个时间点去做任何我们想要做的事情。


HandlerExecutionChain类中以Object引用所声明的handler对象。


HandlerAdapter类的基本接口


SpringMVC中的又一个核心接口,HandlerAdapter:

package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface HandlerAdapter {
  boolean supports(Object handler); 
  ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
  long getLastModified(HttpServletRequest request, Object handler);
}
复制代码


在DispatcherServlet中,除了HandlerMapping实现类的列表,同样也注册了一个HandlerAdapter实现类组成的列表,有代码为证。

/** List of HandlerMappings used by this servlet */
    private List<HandlerMapping> handlerMappings;
/** List of HandlerAdapters used by this servlet */
    private List<HandlerAdapter> handlerAdapters;
复制代码


接下来,我们以DispatcherServlet类:

/**
   * Return the HandlerAdapter for this handler object.
   * @param handler the handler object to find an adapter for
   * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
   */
  protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    for (HandlerAdapter ha : this.handlerAdapters) {
      if (logger.isTraceEnabled()) {
        logger.trace("Testing handler adapter [" + ha + "]");
      }
      if (ha.supports(handler)) {
        return ha;
      }
    }
    throw new ServletException("No adapter for handler [" + handler +
        "]: Does your handler implement a supported interface like Controller?");
  }
复制代码

HandlerExecutionChain中的handler对象会被作为参数传递进去,在DispatcherServlet类中注册的HandlerAdapter实现类列表会被遍历,然后返回第一个supports方法返回true的HandlerAdapter对象,用这个HandlerAdapter实现类中的handle方法处理handler对象,并返回ModelAndView这个包含了视图和数据的对象。


HandlerAdapter就是SpringMVC提供的第三个扩展点,你可以提供自己的实现类来处理handler对象。


ModelAndView是SpringMVC中对视图和数据的一个聚合类。就是由SpringMVC的最后一个核心接口View所抽象:

package org.springframework.web.servlet;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface View {
  String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
  String PATH_VARIABLES = View.class.getName() + ".pathVariables";
  String getContentType();
  void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
复制代码

最后会作为一个Map对象传递到View实现类中的render方法,调用这个render方法,就完成了视图到响应的渲染。这个View实现类,就是来自HandlerAdapter中的handle方法的返回结果。


ModelAndView到真正的View实现类有一个解析的过程,ModelAndView中可以有真正的视图对象,也可以只是有一个视图的名字,SpringMVC会负责将视图名称解析为真正的视图对象。




总结分析


完整的web请求在SpringMVC中的处理过程和其中涉及到的核心类和接口。


在一个典型的SpringMVC调用中,HandlerExecutionChain中封装handler对象就是用@Controller注解标识的类的一个实例,根据类级别和方法级别的@RequestMapping注解,由默认注册的DefaultAnnotationHandlerMapping(后续版本RequestMappingHandlerMapping类,但是为了向后兼容,DefaultAnnotationHandlerMapping也可以使用)生成HandlerExecutionChain对象,再由AnnotationMethodHandlerAdapter(后续版本中更新为RequestMappingHandlerAdapter类,AnnotationMethodHandlerAdapter也可以使用)来执行这个HandlerExecutionChain对象,生成最终的ModelAndView对象后,再由具体的View对象的render方法渲染视图。



相关文章
|
8天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
1月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
1月前
|
存储 监控 Shell
docker的底层原理二:容器运行时环境
本文深入探讨了Docker容器运行时环境的关键技术,包括命名空间、控制组、联合文件系统、容器运行时以及分离的进程树,这些技术共同确保了容器的隔离性、资源控制和可移植性。
38 5
|
1月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
53 2
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
157 2
|
1月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
108 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
2月前
|
XML 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
|
1月前
|
JSON 前端开发 JavaScript
优雅!Spring Boot 3.3 实现职责链模式,轻松应对电商订单流程
本文介绍如何使用 Spring Boot 3.3 实现职责链模式,优化电商订单处理流程。通过将订单处理的各个环节(如库存校验、优惠券核验、支付处理等)封装为独立的处理器,并通过职责链将这些处理器串联起来,实现了代码的解耦和灵活扩展。具体实现包括订单请求类 `OrderRequest`、抽象处理器类 `OrderHandler`、具体处理器实现(如 `OrderValidationHandler`、`VerifyCouponHandler` 等)、以及初始化职责链的配置类 `OrderChainConfig`。
|
3月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
47 0
|
6月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
189 0