SpringBoot内置tomcat启动过程及原理
作者:李岩科
1 背景
SpringBoot是一个框架,一种全新的编程规范,他的产生简化了框架的使用,同时也提供了很多便捷的功能,比如内置tomcat就是其中一项,他让我们省去了搭建tomcat容器,生成war,部署,启动tomcat。因为内置了启动容器,应用程序可以直接通过 Maven 命令将项目编译成可执行的 jar 包,通过 java -jar 命令直接启动,不需要再像以前一样,打包成 War 包,然后部署在 Tomcat 中。那么内置tomcat是如何实现的呢
2 tomcat启动过程及原理
2.1 下载一个springboot项目
在这里下载一个项目https://start.spring.io/ 也可以在idea新建SpringBoot-Web工程.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
点击 pom.xml会有 tomcat依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.1.2.RELEASE</version> <scope>compile</scope> </dependency>
2.2 从启动入口开始一步步探索
点击进入run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } //继续点击进入run方法 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
进入到这个run方法之后就可以看到,我们认识的一些初始化事件。主要的过程也是在这里完成的。
2.3 源码代码流程大致是这样
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); /**1、配置系统属性*/ configureHeadlessProperty(); /**2.获取监听器*/ SpringApplicationRunListeners listeners = getRunListeners(args); /**发布应用开始启动事件 */ listeners.starting(); try { /** 3.初始化参数 */ ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); /** 4.配置环境*/ ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); /**5.创建应用上下文*/ context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); /**6.预处理上下文*/ prepareContext(context, environment, listeners, applicationArguments, printedBanner); /**6.刷新上下文*/ refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } /** 8.发布应用已经启动事件 */ listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { /** 9.发布应用已经启动完成的监听事件 */ listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
代码中主要就是通过 switch 语句,根据 webApplicationType 的类型来创建不同的 ApplicationContext:
- DEFAULT_SERVLET_WEB_CONTEXT_CLASS:Web类型,实例化 AnnotationConfigServletWebServerApplicationContext
- DEFAULT_REACTIVE_WEB_CONTEXT_CLASS:响应式Web类型,实例化 AnnotationConfigReactiveWebServerApplicationContext
- DEFAULT_CONTEXT_CLASS:非Web类型,实例化 AnnotationConfigApplicationContext
2.4 创建完应用上下文之后,我们在看刷新上下文方法
一步步通过断点点击方法进去查看,我们看到很熟悉代码spring的相关代码。
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //初始化前的准备工作,主要是一些系统属性、环境变量的校验,比如Spring启动需要某些环境变量,可以在这个地方进行设置和校验 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. //准备bean工厂 注册了部分类 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //注册bean工厂后置处理器,并解析java代码配置bean定义 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //注册bean后置处理器,并不会执行后置处理器,在后面实例化的时候执行 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. //初始化事件监听多路广播器 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //待子类实现,springBoot在这里实现创建内置的tomact容器 onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
2.5 onRefresh() 方法是调用其子类实现的
也就是 ServletWebServerApplicationContext
/** 得到Servlet工厂 **/ this.webServer = factory.getWebServer(getSelfInitializer());
其中 createWebServer() 方法是用来启动web服务的,但是还没有真正启动 Tomcat,只是通过ServletWebServerFactory 创建了一个 WebServer,继续来看这个 ServletWebServerFactory:
this.webServer = factory.getWebServer(getSelfInitializer()); 这个方法可以看出TomcatServletWebServerFactory的实现。相关Tomcat的实现。
2.6 TomcatServletWebServerFactory 的 getWebServer() 方法
清晰的看到new 出来了一个Tomcat.
2.7 Tomcat创建之后,继续分析Tomcat的相关设置和参数
@Override public WebServer getWebServer(ServletContextInitializer... initializers) { /** 1、创建Tomcat实例 **/ Tomcat tomcat = new Tomcat(); //创建Tomcat工作目录 File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); customizeConnector(connector); /** 2、给创建好的tomcat设置连接器connector **/ tomcat.setConnector(connector); /** 3.设置不自动部署 **/ tomcat.getHost().setAutoDeploy(false); /** 4.配置Tomcat容器引擎 **/ configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } /**准备Tomcat的StandardContext,并添加到Tomcat中*/ prepareContext(tomcat.getHost(), initializers); /** 将创建好的Tomcat包装成WebServer返回**/ return getTomcatWebServer(tomcat); }
2.8 继续点击getTomcatWebServer方法,找到initialize()方法,可以看到tomcat.start();启动tomcat服务方法。
// Start the server to trigger initialization listeners //启动tomcat服务 this.tomcat.start(); //开启阻塞非守护进程 startDaemonAwaitThread();
//Tomcat.java
2.9 TomcatWebServer.java 控制台会打印这句话
Tomcat started on port(s): 8080 (http) with context path ‘’
3 总结
SpringBoot的启动主要是通过实例化SpringApplication来启动的,启动过程主要做了如下几件事情:
配置系统属性、获取监听器,发布应用开始启动事件、初始化参数、配置环境、创建应用上下文、预处理上下文、刷新上下文、再次刷新上下文、发布应用已经启动事件、发布应用启动完成事件。而启动 Tomcat 是刷新上下文 这一步。
Spring Boot 创建 Tomcat 时,会先创建一个上下文,将 WebApplicationContext 传给 Tomcat;
启动 Web 容器,需要调用 getWebserver(),因为默认的 Web 环境就是 TomcatServletWebServerFactory,所以会创建 Tomcat 的 Webserver,这里会把根上下文作为参数给 TomcatServletWebServerFactory 的 getWebServer();启动 Tomcat,调用 Tomcat 中 Host、Engine 的启动方法。
3.1 Tomcat相关名称介绍

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
产品管理不是「听从指挥」,不要再做「废话管理」了!
有一个现象,让我印象很深刻:许多公司会聘请经验丰富的产品专家,希望他们能扩大产品规模。但在实际工作中,这些产品专家却有心无力,因为他们不是真正有决定权的人。 高层管理者总觉得自己很了解应该做什么,并希望产品经理服从他们的命令。所以在现实场景里,产品专家们只能根据指令完成「废话管理 Bullshit Management」,而不是产品管理。 没有决策权,产品经理就无法茁壮成长。 可要小心了!有些公司「说的」和「做的」总是截然相反。说是扁平化管理,但你可能会发现公司内部有层层决策链,最终还会因此陷入分析瘫痪; 你可能会被授予一定的决策自主权,但绝不会超出特定范围。比如你可以敲定如何更好地实施解决方案,但却无法决定先解决哪个问题。 凭借主观想法和管理层级推动业务,是让人震惊的。我特想知道,领导层聘请产品经理究竟是为了执行废话,还是管理产品。 下面我将举例说明什么是「废话管理」,以及产品经理们可以如何避免这些可怕的陷阱。 一、一直被误解,从未被实践的产品管理 随着公司的发展,内部政治化会逐渐加强,而敏捷性则越来越弱。有些时候,最重要事成了取悦内部的关键干系人,而不是弄清楚哪些终端用户的问题值得...
- 下一篇
HarmonyOS 3.1版本发布,全面进入声明式开发
开发者的脚步永不停歇,2022年我们发布了HarmonyOS 3.0 Release版本,为了进一步满足开发者高效开发应用程序的诉求,在同年11月4日华为开发者大会HDC2022上,我们推出了HarmonyOS 3.1版本。 HarmonyOS 3.1 版本主推ArkTS开发语言,ArkTS API的数量也将达到10000+,主要API能力包括:增强的声明式UI能力、全新的应用开发模型——Stage模型,并在DFX、Web组件开发、国际化开发、通信互联、媒体软件等子系统能力方面有所更新或增强,这些能力标志着HarmonyOS全面进入ArkTS语言的声明式开发阶段。 下面,让我们一起了解HarmonyOS 3.1版本主要有哪些关键特性吧。 一、声明式UI能力 ArkUI是一套构建HarmonyOS应用界面的声明式UI开发框架。它通过简洁自然的UI信息描述语法、丰富的UI动效组件和API,以及不断增强的一次开发、多端部署能力,帮助您提升HarmonyOS应用界面开发效率。 目前ArkUI已支持包括Canvas、XComponent、DatePicker等超70个UI组件,并且提供了丰富的响...
相关文章
文章评论
共有0条评论来说两句吧...