缘由

Spring Framework Web (MVC) 是开发中非常常见的框架,深入研究其技术原理,达到知其然知其所以然。

简述

Spring Framework Web (MVC) 是Spring 推出的简单且灵活的Web框架,目前已支持Servlet 3.0 规范,提供程序化配置,不再使用web.xml来配置servlet,本文主要会介绍配置的启动、请求处理流程。

本文将会根据自己对Spring Framework Web的理解编辑。

启动流程

Spring Mvc的DispatchServlet是一个非常重要的类,启动流程会围绕着DispatchSerlvet的创建和servlet注册进行。


DispatcherServlet 类结构

DispatcherServlet 继承 FrameworkServlet 抽象类
FrameworkServlet 继承 HttpServletBean 抽象类
HttpServletBean 继承 HttpServlet 抽象类
HttpServlet 继承 GenericServlet 抽象类
GenericServlet 继承 Servlet、ServletConfig 接口

Servlet、ServletConfig、GenericServlet、HttpServlet 是 Java Servlet 扩展包中的类,Servlet接口定义一些重要的方法,ServletConfig是一个Servlet配置对象,初始化过程中servlet容器可以传递信息到Servlet;GenericServlet定义一个通用的、协议无关的Servlet,把ServletConfig 和 servlet 关联起来;HttpServlet继承了GenericServlet并实现了Http协议,自定义的Sevlet可以直接继承此类,实现其中的goGet()... 方法进行业务处理,不用关系Http协议相关的细节。

HttpServletBean作为 HttpServlet 的实现类,将servlet的配置参数作为 bean properties,实现方式为:实现init()方法,把init_param 设置为 bean properties,另外调用抽象方法 initServletBean();

FrameworkServlet 将Spring的应用上下文与Servlet进行融合,Spring boot web 中会使用有参构造实例,public FrameworkServlet(WebApplicationContext webApplicationContext),webApplication参数不保证已执行 refresh(),此处存在一段容器处理的逻辑 A:

  1. 若webApplication没有父容器,会从ServletContext 中获取父容器设置为 webApplication的父容器。
  2. 若webApplication未分配id,此时会为其分配一个id。
  3. ServletContext 和 ServletConfig 会被委派到 webApplication。
  4. 调用 postProcessWebApplicationContext() 方法。
  5. 通过“contextInitializerClasses” init param 或 通过setContextInitializers属性指定的任何ApplicationContextInitializer将会被应用或调用。
  6. 调用 refresh() 方法。

FrameworkServlet实现initServletBean(),创建WebApplicationContext:

  1. 从ServletConfig中的 ServletContext中获取父容器。
  2. 构造方法中的webApplication参数为不为空时,又实现了 ConfigurableWebApplicationContext 接口,就会实现 逻辑 A。
  3. 若webApplication为空,则会在ServletContext中获取,WebApplicationContext 必须已加载并保存到ServletContext中才可以获取到。
  4. 若webApplication为空,就会进行创建,并从ServletConfig中的 ServletContext中获取父容器。
  5. 若没有收到ContextRefreshedEvent事件,就会调用 onRefresh(WebApplicationContext)方法。

DispatcherServlet 作为 FrameworkServlet 的实现类,主要实现 onRefresh() doService() 两个方法,onRefresh()会执行初始化策略initStrategies(ApplicationContext),doService()会执行请求处理逻辑。

以上介绍了DispatchServlet类的组成,有助于了解其功能实现逻辑。


介绍 DispatcherServlet 初始化逻辑initStrategies(ApplicationContext)

  1. initMultipartResolver(context);
  2. initLocaleResolver(context);
  3. initThemeResolver(context);
  4. initHandlerMappings(context);
  5. initHandlerAdapters(context);
  6. initHandlerExceptionResolvers(context);
  7. initRequestToViewNameTranslator(context);
  8. initViewResolvers(context);
  9. initFlashMapManager(context);

其实现逻辑大致上以 context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class) 相似的方式获取对应的实现类,HandlerMapping、HandlerAdapter的逻辑会有一些稍微的差别,增加了一些排序、默认处理等逻辑。

DispatchServlet 会在第一次web请求时由Tomcat触发创建,调用 initStrategies() 逻辑,并调用 doService() 处理请求。

web请求的处理在 doDispatch(request, response) 中进行:

  1. 获取 HandlerExecutionChain handler处理链 mappedHandler = getHandler(processedRequest); ,循环调用 HandlerMapping 集合 mapping.getHandler(request) , 在 HandlerMapping 实现类 AbstractHandlerMapping.getHandler() 内部执行 handler = getHandlerInternal(request) 交给子类查找Handler, 然后执行 executionChain = getHandlerExecutionChain(handler, request) 将匹配到的 Interceptor 放到 chain 中。
  2. 获取 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()), 循环调用 handlerAdapters 集合 adapter.supports(handler) , 在 AbstractHandlerMethodAdapter 类中查找,若 handler 是 HandlerMethod 类型,执行抽象方法 supportsInternal((HandlerMethod) handler)。
  3. 执行 HandlerExecutionChain 的 mappedHandler.applyPreHandle(processedRequest, response) 方法。
  4. 调用handler处理请求 ha.handle(processedRequest, response, mappedHandler.getHandler())
  5. 执行 HandlerExecutionChain 的 mappedHandler.applyPostHandle(processedRequest, response, mv)方法。

Spring mvc 主要功能类

Handler

HandlerMapping
类说明:接口定义请求和handler对象之间的映射,Spring Mvc框架提供了 BeanNameUrlHandlerMapping 、RequestMappingHandlerMapping,开发者可自定义实现增加自定义的HandlerMapping,通常 handlerMapping 会包装为 HandlerExecutionChain 实例,有可能伴随一些HandlerInterceptor 实例。Dispatcherservlet 会先调用 HandlerInterceptor的preHandle 方法,在调用handler自身的方法。
类作用:定义接口方法:HandlerExecutionChain getHandler(HttpServletRequest request)

AbstractHandlerMapping
类说明:HandlerMapping接口的抽象实现,支持排序、默认handler,handler interceptor、包括基于路径模式的handler interceptor 映射。
类作用:实现 HandlerExecutionChain getHandler(HttpServletRequest request) 接口方法,调用抽象方法 getHanlderInternal(),将返回的handler和对应的 interceptor 包装为 HandlerExecutionChain 返回。

AbstractHandlerMethodMapping
类说明:AbstractHandlerMapping 的抽象子类,定义请求和HandlerMethod 之间的映射。实现 InitializingBean 接口,在子类作为Bean初始化时,执行 initHandlerMethods(),将所有@Controller @RequestMapping 注解的方法全部解析为HandlerMethod。
类作用:初始化HandlerMethod。

RequestMappingInfoHandlerMapping
类说明:AbstractHandlerMethodMapping 的抽象实现类,使用 RequestMappingInfo 定义请求和Handler之间的映射。
类作用:实现 Handler 与 request 之间的匹配逻辑。

RequestMappingHandlerMapping
类说明:创建 RequestMappingInfo 实例,从@Controller 类中方法级的 @RequestMapping 注解 解析为 RequestMappingInfo 实例。
类作用:创建HandlerMethod 和 request 之间的映射关系

HandlerAdapter
类说明:每种handler类型为了能够处理request,必须实现此接口。DipatcherServlet 访问所有handler都是通过此接口,意味着不必包含handler类型的任何指定代码。
类作用:定义 boolean supports(Object handler) 和 handle() ,DispatcherServlet 选择合适的HandlerAdapter时,通过 supports 方法来判断,选中后执行此adapter的handle().

AbstractHandlerMethodAdapter
类说明:HandlerAdapter的抽象实现,支持 HandlerMethod 类型。
类作用:指定handler type 是 HandlerMethod后,实现了 supports() 逻辑,并定义 handleInternal() 抽象方法给子类实现。

RequestMappingHandlerAdapter
类说明:AbstractHandlerMethodAdapter 的扩展,支持 @RequestMapping 注解的 HandlerMethods.
类作用:实现抽象方法 handleInternal(),定义具体的调用逻辑,并处理参数绑定和返回参数转换。


内置Tomcat的运行机制

Spring web 应用的上下文是 AnnotationConfigServletWebServerApplicationContext,其继承了 ServletWebServerApplicationContext。执行 SpringApplication#applicationContext.refresh() 时,会调用ServletWebServerApplicationContext的refresh(),ServletWebServerApplicationContext间接继承了 抽象类AbstractApplicationContext,所以会执行 AbstractApplicationContext#refresh(),此处重点关注 refresh()中的 onfresh(),ServletWebServerApplicationContext实现了此方法并调用 createWebServer(),至此进入创建WebServer的逻辑。

首先会获取 ServletWebServerFactory 实现类 TomcatServletWebServerFactory,然后执行 ServletWebServerFactory#getWebServer() 方法获取 webServer实例。

-- TomcatServletWebServerFactory Bean定义
ServletWebServerFactoryAutoConfiguration 导入 ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class 注册 WebServerFactoryCustomizerBeanPostProcessor\ ErrorPageRegistrarBeanPostProcessor Bean定义。 WebServerFactoryCustomizerBeanPostProcessor 在Bean初始化之前,若Bean属于WebFactory,就会执行WebServerFactoryCustomizer 实现类的 customize() 方法,属于WebFactory定制化的逻辑。大多数的定制逻辑差不多都是一致的,实现定制接口,并注册为Bean。

ServletWebServerFactoryAutoConfiguration 导入 ServletWebServerFactoryConfiguration.EmbeddedTomcat.class 定义 TomcatServletWebServerFactory Bean。

Tomcat启动

Tomcat的启动过程包括以下步骤:

  1. 初始化Catalina:在Tomcat类中,会初始化一个Catalina实例。Catalina是Tomcat的核心容器,用于管理各个组件和模块。
  2. 加载配置文件:Tomcat在启动时需要加载配置文件,通常是server.xml,其中包含了Tomcat服务器的配置信息,如端口号、连接池设置等。这些配置文件会被解析成对应的Java对象,并由Catalina进行管理。
  3. 创建服务和连接器:Tomcat根据配置文件中的信息创建一个或多个服务(Service)和连接器(Connector)。服务表示Tomcat的一个实例,而连接器用于处理与客户端的连接。每个连接器通常使用一个 独立的线程池来处理客户端请求。
  4. 初始化组件:Tomcat会初始化一系列组件,包括Engine、Host、Context等。Engine表示整个Tomcat服务器,Host表示一个虚拟主机,Context表示一个Web应用程序上下文。
  5. 加载Web应用程序:Tomcat在启动时会加载预配置的Web应用程序。这些应用程序通常存储在webapps目录下。Tomcat会为每个应用程序创建一个Context对象,并将其添加到对应的Host中。
  6. 启动Web应用程序:在Tomcat启动过程中,会初始化并启动预加载的Web应用程序,即调用Web应用程序的init()方法。
  7. 启动连接器:Tomcat会启动之前创建的连接器,开始监听指定的端口,等待客户端请求的到来。
  8. 等待请求:Tomcat启动完成后,进入等待状态,等待客户端发送请求。

DispatcherServlet 如何部署到Tomcat当中?

DispatcherServletAutoConfiguration 配置类中有几个子配置类,比如定义DispatcherServlet和MultipartResolver 的 DispatcherServletConfiguration配置类,还有定义 DispatcherServletRegistrationBean 的 DispatcherServletRegistrationConfiguration 配置类,DispatcherServletRegistrationBean负责DispatcherServlet 的部署。

DispatcherServletRegistrationBean 类继承ServletRegistrationBean,在构造方法中调用父类的构造方法和 设置应用请求上下文的addUrlMappings()。
ServletRegistrationBean 类继承 DynamicRegistrationBean,实现父类的抽象方法 addRegistration(ServletContext),执行servletContext.addServlet(name, this.servlet),将DispatcherServlet 注册到 Servlet3.0容器当中。

DynamicRegistrationBean 类继承 RegistrationBean,实现父类的抽象方法 register(String description, ServletContext servletContext),执行抽象方法 addServlet()。register()的执行调用是在 RegistrationBean 的onStartUp() 中进行,此方法也是父类 ServletContextInitializer 的实现。

ServletContextInitializer 接口用于程序化配置Servlet3.0上下文,设计逻辑与 ServletContainerInitializer 相似,但是并不是由Servlet container控制,而是由Spring进行管理,但是又不是与 SCI 完全没有关系,启动Tomcat时,会以ServletWebServerApplicationContext#selfInitialize作为 ServletContextInitializer 的实现设置到 TomcatStarter 对象中,TomcatStarter 则是 ServletContainerInitializer 的子类,以SCI的角色注册到 Tomcat 中,Tomcat启动过程中会执行SCI机制,TomcatStarter的 onStartUp()方法则会执行

public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
    for (ServletContextInitializer initializer : this.initializers) {
        // ServletWebServerApplicationContext#selfInitialize 则会在此调用
        initializer.onStartup(servletContext);
    }
}

ServletWebServerApplicationContext#selfInitialize 会将Spring中所有ServletContextInitializer子类Bean,遍历调用。

private void selfInitialize(ServletContext servletContext) throws ServletException {
    prepareWebApplicationContext(servletContext);
    registerApplicationScope(servletContext);
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        // DispatcherServletRegistrationBean 会将DispacherServlet 动态的注册到 Tomcat 当中
        beans.onStartup(servletContext);
    }
}

*DispatcherServlet部署到Tomcat的过程中,利用sci、Servlet3.0动态注册Servlet等Servlet相关的技术,另外扩展了 SCI 机制,利用 ServletContextInitializer 接口,加入了Spring特性。

Spring Mvc 配置类

@EnableWebMvc

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

类说明:SpringMvc 基于注解配置的重要类,注解EnableWebMvc可以添加到 @Configuration 注解的类上,可以开启Spring Mvc的自动配置。Spring Bean 实现 WebMvcConfigurer 接口进行自定义Spring Mvc配置,若需要更高级的设置,可使用 WebMvcConfigurationSupport 或者 DelegatingWebMvcConfiguration 同时移除 @EnableWebMvc 注解。
类作用:导入 DelegatingWebMvcConfiguration 配置类。

DelegatingWebMvcConfiguration
类说明:WebMvcConfigurationSupport 的子类,本身作为一个配置类,利用setter注入将WebMvcConfigurer类型的Bean注入到 WebMvcConfigurerComposite 中,由 WebMvcConfigurerComposite 负责在 WebMvcConfigurer 的接口实现方法中,循环调用,WebMvcConfigurerComposite 接口实现方法全部在 DelegatingWebMvcConfiguration 中调用。
类作用:重写 WebMvcConfigurationSupport 方法,并调用 WebMvcConfigurerComposite 方法,实现定制Spring Mvc的逻辑。
补充说明:采用委派模式,收到 WebMvcConfigurationSupport 任务后,交给 WebMvcConfigurerComposite 执行具体调用。

WebMvcConfigurerComposite
类说明:WebMvcConfigurer 的实现类,采用了组合设计模式,实现 WebMvcConfigurer 接口,并内置一个 WebMvcConfigurer 集合属性,在接口实现方法中循环调用,并以WebMvcConfigurer 实现类的身份在 DelegatingWebMvcConfiguration 实现接收调用。
类作用:接收 DelegatingWebMvcConfiguration 任务,执行具体调用。

WebMvcConfigurationSupport
类说明:实现 ApplicationContextAware, ServletContextAware 接口,在子类中以配置类的角色注册到 Spring Ioc 中,并获得 ServletContext、ApplicationContext Bean。提供Java配置 Mvc 的重要类,注册 HandlerMappings HandlerAdapters HandlerExceptionREsolverComposite AntPathMatcher UrlPathHelper。
类作用:Java 配置 Mvc,注册各个功能类到Spring IOC.

补充:

  1. 自定义SpringMvc配置时,可以通过实现WebMvcConfigurer接口,实现指定的接口即可,启动时会自动搜索其实现类进行处理。另外可通过继承 DelegatingWebMvcConfiguration 或者 WebMvcConfigurationSupport 配置更加高级的参数。
  2. WebMvcConfigurationSupport 类中注册了 Spring Mvc 所需要的所有组件,可通过继承此类进行扩展。主要分为 HandlerMappings、HandlerAdapters、 HandlerExceptionResolverComposite、AntPathMatche、UrlPathHelper、HttpMessageConverters 等等。
  3. Spring Boot 中的WebMvcAutoConfiguration可以替换掉@EnableMvc注解,类中的子类 WebMvcAutoConfigurationAdapter 继承了 WebMvcConfigurer, 并导入 EnableWebMvcConfiguration 类,此类继承的是 DelegatingWebMvcConfiguration,覆盖了父类中 Spring Mvc 主要组件的创建,主要是利用Spring boot 获取到的参数传递到组件创建方法。
  4. Spring Boot 中的WebMvcAutoConfiguration 自动配置顺序在 DispatcherServletAutoConfiguration 之后,DispatcherServletAutoConfiguration 在 ServletWebServerFactoryAutoConfiguration 之后,此顺序可以理解为 先自动配置 内置服务器,再配置 DispatcherServlet,然后才是 WebMvc 的配置。

Spring Mvc DispatcherServlet 注册到Tomcat 相关类。

ServletWebServerFactoryConfiguration
类说明:servlet web servers的配置类,在 ServletWebServerFactoryAutoConfiguration 使用@Import注解导入。
类作用:注册 TomcatServletWebServerFactory 作为Bean。

TomcatServletWebServerFactory
类说明:AbstractServletWebServerFactory 实现类,被用来创建TomcatWebServer,可以被Spring的 ServletContextInitializer 或者 Tomcat LifecycleListeners 初始化。
类作用:创建 WebServer。

核心代码

    public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }
        Tomcat tomcat = new Tomcat();
        File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        tomcat.getService().addConnector(connector);
        customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        configureEngine(tomcat.getEngine());
        for (Connector additionalConnector : this.additionalTomcatConnectors) {
            tomcat.getService().addConnector(additionalConnector);
        }
        // prepareContext() -> configureContext() 创建 TomcatStarter 并设置到TomcatEmbeddedContext.starter中
        prepareContext(tomcat.getHost(), initializers);
        // 创建 TomcatWebServer 并执行 initialize() -> Tomcat.start() -> StandardServer.start() -> startInternal() -> StandardService.start() -> startInternal() -> StandardEngine.startInternal() - 异步 -> StandardContext.startInternal() -> 循环执行 initializers 中元素的 onStartUp() -> TomcatStarter.onStartup() -> 开始执行 ServletContextInitializer 实现类的 onStartup().
        return getTomcatWebServer(tomcat);
    }

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize

    private void selfInitialize(ServletContext servletContext) throws ServletException {
        prepareWebApplicationContext(servletContext);
        registerApplicationScope(servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
        // 执行 ServletContextInitializer 实现类,比如:DispatcherServletRegistrationBean。
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
            beans.onStartup(servletContext);
        }
    }

ServletWebServerApplicationContext
类说明:AnnotationConfigServletWebServerApplicationContext 会实现此类,实现父类 GenericWebApplicationContext 的 onRefresh() 调用 createWebServer()。
类作用:调用 createWebServer()。

ServletWebServerApplicationContext
类说明:一个 WebApplicationContext,可从容器Bean ServletWebServerFactory 中自动引导。此context 通过搜索一个单独 ServletWebServerFactory bean 来创建、初始化、运行一个web server。另外context中定义的所有Sevlet Filter bean 将会被自动注册到web server中。
类作用:实现onFresh()方法,调用createWebServer().

DispatcherServletConfiguration 注册 DispatcherServlet 到Spring Bean 容器:

  DispatcherServletAutoConfiguration {
    DispatcherServletConfiguration {
      new DispatcherServlet()
    }
  }

DispatcherServletRegistrationBean
类说明:ServletRegistrationBean子类,负责对DispatcherServlet自动注入,注册并暴露 DispatcherServletPath 信息。
类作用:

  1. ServletRegistrationBean 是DynamicRegistrationBean子类,实现 addRegistration(ServletContext) 抽象方法,可将自身的servlet添加到web 上下文中(ervletContext.addServlet(name, this.servlet))。
  2. DynamicRegistrationBean是RegistrationBean的子类,实现 register(ServletContext) 抽象方法,执行 addRegistration() 抽象方法。
  3. RegistrationBean 是 ServletContextInitializer 的抽象实现类,实现onStartup(ServletContext)接口方法,调用 register() 抽象方法。

总结

Spring Mvc 作为非常常见的技术,对其中的主要逻辑设计学习了解,可以更好的掌控开发。


Mario
56 声望5 粉丝

方向大于努力,选择方向总有个期限,过了期限还要再考虑方向问题,岂不是自增烦恼