SpringMVC的启动入口在SpringServletContainerInitializer类,它是ServletContainerInitializer实现类(Servlet3.0新特性)。在实现方法中使用WebApplicationInitializer创建ApplicationContext、创建注册DispatcherServlet、初始化ApplicationContext等。

SpringMVC已经将大部分的启动逻辑封装在了几个抽象WebApplicationInitializer中,开发者只要继承这些抽象类实现抽象方法即可。

本文将详细分析ServletContainerInitializer、SpringServletContainerInitializer和WebApplicationInitializer的工作流程。

Servlet3.0的ServletContainerInitializer

ServletContainerInitializer接口

ServletContainerInitializer是Servlet3.0的接口。

该接口在web应用程序启动阶段接收通知,注册servlet、filter、listener等。

该接口的实现类可以用HandlesTypes进行标注,并指定一个Class值,后续会将实现、扩展了这个Class的类集合作为参数传递给onStartup方法。

以tomcat为例,在容器配置初始化阶段,将使用SPI查找实现类,在ServletContext启动阶段,初始化并调用onStartup方法来进行ServletContext的初始化。

SpringServletContainerInitializer实现类

在onStartup方法创建所有的WebApplicationInitializer对并调用onStartup方法。

以下为SPI配置,在spring-web/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer文件:

WebApplicationInitializer接口

WebApplicationInitializer接口概述

Interface to be implemented in Servlet 3.0+ environments in order to configure the ServletContext programmatically -- as opposed to (or possibly in conjunction with) the traditional web.xml-based approach.

Implementations of this SPI will be detected automatically by SpringServletContainerInitializer, which itself is bootstrapped automatically by any Servlet 3.0 container. See its Javadoc for details on this bootstrapping mechanism.

示例:

public class MyWebAppInitializer implements WebApplicationInitializer {

   @Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class); // Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext)); // Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext =
new AnnotationConfigWebApplicationContext();
dispatcherContext.register(DispatcherConfig.class); // Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}

开发者可以编写类继承AbstractAnnotationConfigDispatcherServletInitializer抽象类,这个抽象类已经将大部分的初始化逻辑进行了封装。

WebApplicationInitializer实现和继承关系

AbstractContextLoaderInitializer类:

Convenient base class for WebApplicationInitializer implementations that register a ContextLoaderListener in the servlet context. The only method required to be implemented by subclasses is createRootApplicationContext(), which gets invoked from registerContextLoaderListener(ServletContext).

AbstractDispatcherServletInitializer类:

Base class for WebApplicationInitializer implementations that register a DispatcherServlet in the servlet context. Most applications should consider extending the Spring Java config subclass AbstractAnnotationConfigDispatcherServletInitializer.

AbstractAnnotationConfigDispatcherServletInitializer类:

WebApplicationInitializer to register a DispatcherServlet and use Java-based Spring configuration.

Implementations are required to implement:

  • getRootConfigClasses() -- for "root" application context (non-web infrastructure) configuration.
  • getServletConfigClasses() -- for DispatcherServlet application context (Spring MVC infrastructure) configuration.

If an application context hierarchy is not required, applications may return all configuration via getRootConfigClasses() and return null from getServletConfigClasses().

开发者SpringMvcInitializer示例

开发者需要编写类继承AbstractAnnotationConfigDispatcherServletInitializer类,实现几个抽象方法:

public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

  /**
* 指定创建root application context需要的@Configuration/@Component类
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{AppConfig.class};
} /**
* 指定创建Servlet application context需要的@Configuration/@Component类
* 如果所有的配置类都使用root config classes就返回null
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return null;
} /**
* Specify the servlet mapping(s) for the DispatcherServlet — for example "/", "/app", etc.
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}

SpringMVC启动流程

入口AbstractDispatcherServletInitializer.onStartup方法

public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
registerDispatcherServlet(servletContext);
}

父类的onStartup方法创建RootApplicationContext、注册ContextLoaderListener监听器:

public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
} protected void registerContextLoaderListener(ServletContext servletContext) {
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
// ContextLoaderListener是ServletContextListener
// 会在contextInitialized阶段初始化RootApplicationContext
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
}

注册DispatcherServlet

registerDispatcherServlet方法用于创建ServletApplicationContext、注册DispatcherServlet:

protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName(); // 创建ServletApplicationContext
WebApplicationContext servletAppContext = createServletApplicationContext(); // 创建DispatcherServlet
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
// 添加ApplicationContextInitializer集,会在初始化时调用
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); // 添加到ServletContext
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported()); Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
} customizeRegistration(registration);
}

触发ContextLoaderListener监听器contextInitialized事件

这个是Servlet的ServletContextListener机制,在ServletContext创建之后触发contextInitialized事件:

public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
} public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 判断是否已经在当前ServletContext绑定了WebApplicationContext
// 如果已经绑定抛错
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException("已经初始化过了");
} try {
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// refresh
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext
.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
} else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
} return this.context;
} catch (RuntimeException | Error ex) {
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
} protected void configureAndRefreshWebApplicationContext(
ConfigurableWebApplicationContext wac, ServletContext sc) {
// 给wac设置id wac.setServletContext(sc);
// 设置spring主配置文件路径
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
} ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
} customizeContext(sc, wac);
// 刷新ApplicationContext
wac.refresh();
}

触发DispatcherServlet的init事件

Servlet在接收请求之前会调用其init方法进行初始化,这个是Servlet的规范。

init方法在其父类HttpServletBean中:

public final void init() throws ServletException {

	// 从ServletConfig获取配置设置到Servlet
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException ex) {
throw ex;
}
} // 初始化
initServletBean();
} // FrameworkServlet
protected final void initServletBean() throws ServletException {
try {
// 初始化ServletApplicationContext
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
} catch (ServletException | RuntimeException ex) {
throw ex;
}
} protected WebApplicationContext initWebApplicationContext() {
// 获取rootApplicationContext
// 之前的ServletContext初始化阶段已经绑定
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null; if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// 将rootApplicationContext设置为Parent
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
// 刷新ApplicationContext
configureAndRefreshWebApplicationContext(cwac);
}
}
}
// 如果没有需要查找或创建
if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
wac = createWebApplicationContext(rootContext);
} if (!this.refreshEventReceived) {
synchronized (this.onRefreshMonitor) {
// 子类实现
onRefresh(wac);
}
} if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
} return wac;
} // DispatcherServlet
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
} // 初始化SpringMVC相关组件
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}

SpringMVC启动流程总结

  • 创建RootApplicationContext、注册ContextLoaderListener监听器
  • 创建ServletApplicationContext、注册DispatcherServlet
  • 触发ContextLoaderListener监听器contextInitialized事件,初始化RootApplicationContext
  • 触发DispatcherServlet的init事件,初始化ServletApplicationContext

spring启动流程 (6完结) springmvc启动流程的更多相关文章

  1. Spring框架系列(5) - 深入浅出SpringMVC请求流程和案例

    前文我们介绍了Spring框架和Spring框架中最为重要的两个技术点(IOC和AOP),那我们如何更好的构建上层的应用呢(比如web 应用),这便是SpringMVC:Spring MVC是Spri ...

  2. SpringMVC启动过程详解(li)

    通过对SpringMVC启动过程的深入研究,期望掌握Java Web容器启动过程:掌握SpringMVC启动过程:了解SpringMVC的配置文件如何配置,为什么要这样配置:掌握SpringMVC是如 ...

  3. SpringMVC学习笔记一(请求流程和配置,启动项目)

    springmvc请求流程: 1.用户发送请求至前端控制器DispatcherServlet 2.DispatcherServlet收到请求调用HandlerMapping处理器映射器. 3.处理器映 ...

  4. SpringMVC 启动流程

    首先看一下Web应用部署初始化过程 (Web Application Deployement),官方文档说明: Web Application Deployment When a web applic ...

  5. SpringMVC启动和执行流程

    Spring框架大家用得很多,相当熟悉,但是我对里面的运作比较好奇,例如bean的加载和使用,和我们定义的配置文件有什么联系;又例如aop在什么时候起作用,原理又是怎样.经过一个了解后,整理了启动和执 ...

  6. SpringMVC 运行流程以及与Spring 整合

    1. 运行流程 2. Spring 和 SpringMVC 整合 // 1. 导入 jar 包 // 2. 配置 web.xml <!-- 配置 Spring 的核心监听器 --> < ...

  7. DM365视频处理流程/DM368 NAND Flash启动揭秘

    出自http://blog.csdn.net/maopig/article/details/7029930 DM365的视频处理涉及到三个相关处理器,分别是视频采集芯片.ARM处理器和视频图像协处理器 ...

  8. 1.1(Spring MVC学习笔记)初识SpringMVC及SpringMVC流程

    一.Spring MVC Spring MVC是Spring提供的一个实现了web MVC设计模式的轻量级Web框架. Spring优点:网上有,此处不复述. 二.第一个Spring MVC 2.1首 ...

  9. Unary模式下客户端创建 default-executor 和 resolver-executor 线程和从启动到执行grpc_connector_connect的主要流程

    (原创)C/C/1.25.0-dev grpc-c/8.0.0, 使用的例子是自带的例子GreeterClient 创建 default-executor 和 resolver-executor 线程 ...

  10. SpringMVC 工作流程

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/baidu_36697353/article/details/64444147 SpringMVC 工 ...

随机推荐

  1. 使用jvm工具排查系统问题

    java-jvm-tool Jstatd 远程连接(推荐) 不用重启项目 远程机配置 [demo@localhost jvmtest]$ vi jstatd.all.policy# 内容grant c ...

  2. 数字孪生和GIS结合将会为智慧园区带来怎样的改变?

    数字孪生和地理信息系统(GIS)是当今智慧园区发展中的两个重要技术,它们的结合将为智慧园区带来根本性的改变和巨大的发展机遇.这种结合将深刻影响园区的规划.建设.运营和管理,为人们创造更智能.高效.可持 ...

  3. Java多线程学习(Day01)

    目录 线程简介 线程实现(重点) 线程状态 线程同步(重点) 线程通信问题 进程与线程概念 --来自百度百科的解释: 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资 ...

  4. spring是否线程安全

    spring 管理的bean默认是单例的,可通过 scope 属性设置scope="singleton" 默认是单例,可修改为scope="prototype" ...

  5. Volcano 原理、源码分析(一)

    0. 总结前置 1. 概述 2. Volcano 核心概念 2.1 认识 Queue.PodGroup 和 VolcanoJob 2.2. Queue.PodGroup 和 VolcanoJob 的关 ...

  6. 珍藏网站-关于路由器、WIFI协议等

    路由器详解:为什么不要买AX3000路由器 https://zhuanlan.zhihu.com/p/403855533 包含以下专业名词和相关话题: RX/TX和MU-MIMO 20MHz/40MH ...

  7. 玩转Python:处理音频文件,两个非常重要的库,很实用,附代码

    pyaudio和sounddevice都是用于Python中音频处理和流的库,允许用户通过他们的API录制.播放和处理音频数据.下面是对这两个库的简要介绍: PyAudio PyAudio 提供了 P ...

  8. Log4j2 漏洞复现GetShell

    目录: 一.搭建环境 1. 首先拉一个docker镜像 2. 然后启动环境 二.获取shell 首先,试验一下DNSLog 1. 准备JNDI注入工具 下载 进入目录打包成jar包 2. 利用 生成p ...

  9. 新版的Django中的path不能使用正则表达式

    新版的path 虽然 取代了 之前的url,但是在写路由的时候不能在路由中直接写正则表达式,不然会找不到页面. 解决方法使用 re_path from django.urls import re_pa ...

  10. Java数组中常见的方法

    一.前言 代码: //给定一个数组 int[] arr = {234,312,32,1321,321,43}; int[] arr1 = new int[6]; int[] arr2 = {1,3,7 ...