spring启动流程 (6完结) springmvc启动流程
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启动流程的更多相关文章
- Spring框架系列(5) - 深入浅出SpringMVC请求流程和案例
前文我们介绍了Spring框架和Spring框架中最为重要的两个技术点(IOC和AOP),那我们如何更好的构建上层的应用呢(比如web 应用),这便是SpringMVC:Spring MVC是Spri ...
- SpringMVC启动过程详解(li)
通过对SpringMVC启动过程的深入研究,期望掌握Java Web容器启动过程:掌握SpringMVC启动过程:了解SpringMVC的配置文件如何配置,为什么要这样配置:掌握SpringMVC是如 ...
- SpringMVC学习笔记一(请求流程和配置,启动项目)
springmvc请求流程: 1.用户发送请求至前端控制器DispatcherServlet 2.DispatcherServlet收到请求调用HandlerMapping处理器映射器. 3.处理器映 ...
- SpringMVC 启动流程
首先看一下Web应用部署初始化过程 (Web Application Deployement),官方文档说明: Web Application Deployment When a web applic ...
- SpringMVC启动和执行流程
Spring框架大家用得很多,相当熟悉,但是我对里面的运作比较好奇,例如bean的加载和使用,和我们定义的配置文件有什么联系;又例如aop在什么时候起作用,原理又是怎样.经过一个了解后,整理了启动和执 ...
- SpringMVC 运行流程以及与Spring 整合
1. 运行流程 2. Spring 和 SpringMVC 整合 // 1. 导入 jar 包 // 2. 配置 web.xml <!-- 配置 Spring 的核心监听器 --> < ...
- DM365视频处理流程/DM368 NAND Flash启动揭秘
出自http://blog.csdn.net/maopig/article/details/7029930 DM365的视频处理涉及到三个相关处理器,分别是视频采集芯片.ARM处理器和视频图像协处理器 ...
- 1.1(Spring MVC学习笔记)初识SpringMVC及SpringMVC流程
一.Spring MVC Spring MVC是Spring提供的一个实现了web MVC设计模式的轻量级Web框架. Spring优点:网上有,此处不复述. 二.第一个Spring MVC 2.1首 ...
- Unary模式下客户端创建 default-executor 和 resolver-executor 线程和从启动到执行grpc_connector_connect的主要流程
(原创)C/C/1.25.0-dev grpc-c/8.0.0, 使用的例子是自带的例子GreeterClient 创建 default-executor 和 resolver-executor 线程 ...
- SpringMVC 工作流程
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/baidu_36697353/article/details/64444147 SpringMVC 工 ...
随机推荐
- Selenium浏览器属性、提取数据
浏览器属性 在使用selenium过程中,实例化driver对象后,driver对象有一些常用的属性和方法 driver.page_source 当前标签页浏览器渲染之后的网页源代码. driver. ...
- 反向 Debug 了解一下?揭秘 Java DEBUG 的基本原理
Debug 的时候,都遇到过手速太快,直接跳过了自己想调试的方法.代码的时候吧-- 一旦跳过,可能就得重新执行一遍,准备数据.重新启动可能几分钟就过去了. 好在IDE 们都很强大,还给你后悔的机会,可 ...
- Python——第二章:range()的用法:
在Python中,range()是一个内置函数,用于生成一个整数序列,通常用于循环遍历.以下是range()函数的一些常见用法: range(n): 从0数到n. 不包含nrange(m, n): 从 ...
- Kubernetes架构及安装
K8s架构 k8s内部是有几个组件的,分别是controller manager,api-server,scheduler,kubelet以及etcd,kube-proxy还有k8s客户端kubect ...
- shutdown详解
linux下shutdown命令详解 shutdown命令安全地将系统关机. 有些用户会使用直接断掉电源的方式来关闭linux,这是十分危险的.因为linux与windows不同,其后台运行着许多进程 ...
- 2021-01-04:mysql里的innodb引擎的数据结构,你有看过吗?
福哥答案2021-01-04: 面试官刚开始问我看过mysql源码没,然后问了这个问题.回答B+树,过不了面试官那关. 答案来自<MySQL技术内幕 InnoDB存储引擎 第2版>第四章, ...
- 小熊派开发实践丨漫谈LiteOS之传感器移植
摘要:本文基于小熊派开发板简单介绍了如何在LiteOS中移植传感器,从而实现对于传感器的相关控制. 1 hello world 相信大家无论在学习编程语言开始的第一个函数应该是HelloWorld,本 ...
- 云小课|3种常用Git工作流推荐
本文分享自华为云社区<[云小课]应用平台第44课 常用Git工作流推荐>,作者: 应用万花筒. . 1. Git工作流-动静有法 简单来说,工作流就是开发团队预置的开发流程和解决问题时使用 ...
- Java变异出现错误:No enclosing instance of type XXX is accessible
摘要:写java代码时遇到下面的编译错误. 本文分享自华为云社区<Java中出现No enclosing instance of type XXX is accessible问题>,作者: ...
- Flutter加固原理及加密处理
引言 为了保护Flutter应用免受潜在的漏洞和攻击威胁,加固是必不可少的措施之一.Flutter加固原理主要包括代码混淆.数据加密.安全存储.反调试与反分析.动态加载和安全通信等多个方面.通过综 ...