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 工程.

1<dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-web</artifactId> 4</dependency>

点击 pom.xml 会有 tomcat 依赖

1<dependency> 2<groupId>org.springframework.boot</groupId> 3<artifactId>spring-boot-starter-tomcat</artifactId> 4<version>2.1.2.RELEASE</version> 5<scope>compile</scope> 6</dependency>

2.2 从启动入口开始一步步探索

点击进入 run 方法

1public static ConfigurableApplicationContext run(Class<?> primarySource, 2String... args) { 3return run(new Class<?>[] { primarySource }, args); 4} 5 6//继续点击进入run方法 7public static ConfigurableApplicationContext run(Class<?>[] primarySources, 8String[] args) { 9return new SpringApplication(primarySources).run(args); 10}

进入到这个 run 方法之后就可以看到,我们认识的一些初始化事件。主要的过程也是在这里完成的。

2.3 源码代码流程大致是这样

1/** 2* Run the Spring application, creating and refreshing a new 3* {@link ApplicationContext}. 4* @param args the application arguments (usually passed from a Java main method) 5* @return a running {@link ApplicationContext} 6*/ 7public ConfigurableApplicationContext run(String... args) { 8StopWatch stopWatch = new StopWatch(); 9stopWatch.start(); 10ConfigurableApplicationContext context = null; 11Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); 12/**1、配置系统属性*/ 13configureHeadlessProperty(); 14/**2.获取监听器*/ 15SpringApplicationRunListeners listeners = getRunListeners(args); 16/**发布应用开始启动事件 */ 17listeners.starting(); 18try { 19/** 3.初始化参数 */ 20ApplicationArguments applicationArguments = new DefaultApplicationArguments( 21args); 22/** 4.配置环境*/ 23ConfigurableEnvironment environment = prepareEnvironment(listeners, 24applicationArguments); 25 26configureIgnoreBeanInfo(environment); 27Banner printedBanner = printBanner(environment); 28/**5.创建应用上下文*/ 29context = createApplicationContext(); 30exceptionReporters = getSpringFactoriesInstances( 31SpringBootExceptionReporter.class, 32new Class[] { ConfigurableApplicationContext.class }, context); 33/**6.预处理上下文*/ 34prepareContext(context, environment, listeners, applicationArguments, 35printedBanner); 36 37/**6.刷新上下文*/ 38refreshContext(context); 39afterRefresh(context, applicationArguments); 40stopWatch.stop(); 41if (this.logStartupInfo) { 42new StartupInfoLogger(this.mainApplicationClass) 43.logStarted(getApplicationLog(), stopWatch); 44} 45/** 8.发布应用已经启动事件 */ 46listeners.started(context); 47callRunners(context, applicationArguments); 48} 49catch (Throwable ex) { 50handleRunFailure(context, ex, exceptionReporters, listeners); 51throw new IllegalStateException(ex); 52} 53 54try { 55/** 9.发布应用已经启动完成的监听事件 */ 56listeners.running(context); 57} 58catch (Throwable ex) { 59handleRunFailure(context, ex, exceptionReporters, null); 60throw new IllegalStateException(ex); 61} 62return context; 63}

代码中主要就是通过 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 的相关代码。

1@Override 2public void refresh() throws BeansException, IllegalStateException { 3synchronized (this.startupShutdownMonitor) { 4// Prepare this context for refreshing. 5//初始化前的准备工作,主要是一些系统属性、环境变量的校验,比如Spring启动需要某些环境变量,可以在这个地方进行设置和校验 6prepareRefresh(); 7 8// Tell the subclass to refresh the internal bean factory. 9ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 10 11// Prepare the bean factory for use in this context. 12//准备bean工厂 注册了部分类 13prepareBeanFactory(beanFactory); 14 15try { 16// Allows post-processing of the bean factory in context subclasses. 17postProcessBeanFactory(beanFactory); 18 19// Invoke factory processors registered as beans in the context. 20//注册bean工厂后置处理器,并解析java代码配置bean定义 21invokeBeanFactoryPostProcessors(beanFactory); 22 23// Register bean processors that intercept bean creation. 24//注册bean后置处理器,并不会执行后置处理器,在后面实例化的时候执行 25registerBeanPostProcessors(beanFactory); 26 27// Initialize message source for this context. 28initMessageSource(); 29 30// Initialize event multicaster for this context. 31//初始化事件监听多路广播器 32initApplicationEventMulticaster(); 33 34// Initialize other special beans in specific context subclasses. 35//待子类实现,springBoot在这里实现创建内置的tomact容器 36onRefresh(); 37 38// Check for listener beans and register them. 39registerListeners(); 40 41// Instantiate all remaining (non-lazy-init) singletons. 42finishBeanFactoryInitialization(beanFactory); 43 44// Last step: publish corresponding event. 45finishRefresh(); 46} 47 48catch (BeansException ex) { 49if (logger.isWarnEnabled()) { 50logger.warn("Exception encountered during context initialization - " + 51"cancelling refresh attempt: " + ex); 52} 53 54// Destroy already created singletons to avoid dangling resources. 55destroyBeans(); 56 57// Reset 'active' flag. 58cancelRefresh(ex); 59 60// Propagate exception to caller. 61throw ex; 62} 63 64finally { 65// Reset common introspection caches in Spring's core, since we 66// might not ever need metadata for singleton beans anymore... 67resetCommonCaches(); 68} 69} 70}

2.5 onRefresh () 方法是调用其子类实现的

也就是 ServletWebServerApplicationContext

1/** 得到Servlet工厂 **/ 2this.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 的相关设置和参数

1@Override 2public WebServer getWebServer(ServletContextInitializer... initializers) { 3 4/** 1、创建Tomcat实例 **/ 5Tomcat tomcat = new Tomcat(); 6//创建Tomcat工作目录 7File baseDir = (this.baseDirectory != null) ? this.baseDirectory 8: createTempDir("tomcat"); 9tomcat.setBaseDir(baseDir.getAbsolutePath()); 10Connector connector = new Connector(this.protocol); 11tomcat.getService().addConnector(connector); 12customizeConnector(connector); 13/** 2、给创建好的tomcat设置连接器connector **/ 14tomcat.setConnector(connector); 15/** 3.设置不自动部署 **/ 16tomcat.getHost().setAutoDeploy(false); 17/** 4.配置Tomcat容器引擎 **/ 18configureEngine(tomcat.getEngine()); 19for (Connector additionalConnector : this.additionalTomcatConnectors) { 20tomcat.getService().addConnector(additionalConnector); 21} 22/**准备Tomcat的StandardContext,并添加到Tomcat中*/ 23prepareContext(tomcat.getHost(), initializers); 24/** 将创建好的Tomcat包装成WebServer返回**/ 25return getTomcatWebServer(tomcat); 26}

2.8 继续点击 getTomcatWebServer 方法,找到 initialize () 方法,可以看到 tomcat.start (); 启动 tomcat 服务方法。

1// Start the server to trigger initialization listeners 2//启动tomcat服务 3this.tomcat.start(); 4//开启阻塞非守护进程 5startDaemonAwaitThread();

//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 相关名称介绍

点赞
收藏

评论区

加载中...

相关推荐

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

SpringBoot连接常见设置

SpringBoot集成了Servlet容器,因此我们可以方便的启动一个容器,并访问其中的接口。那么如何控制Servlet容器中的线程池呢?可以通过如下配置进行控制server:tomcat:appcectcount:1minspacethreads:1maxthreads:

SpringBoot打成war包,部署到Tomcat服务器

前言 SpringBoot默认达成jar包,默认使用内置的Tomcat。但考虑到项目需要打成war包部署到外部的Tomcat服务器中。把SpringBoot项目按照平常的web项目一样发布到tomcat容器下。1、修改pom.xml文件将默认的jar(默认创建项目的时候没有packaging)方式改为war。

Tomcat在SpringBoot中是如何启动的

!image(https://cdn.pixabay.com/photo/2017/09/29/11/57/cat2798804_1280.png)前言我们知道SpringBoot给我们带来了一个全新的开发体验,我们可以直接把web程序达成jar包,直接启动,这就得益于SpringBoot内置了容器,可以直接启动,本文将以Tomcat为例

SpringBoot打成war包,部署到Tomcat服务器

前言 SpringBoot默认达成jar包,默认使用内置的Tomcat。但考虑到项目需要打成war包部署到外部的Tomcat服务器中。把SpringBoot项目按照平常的web项目一样发布到tomcat容器下。1、修改pom.xml文件将默认的jar(默认创建项目的时候没有packaging)方式改为war。

Java初级开发0608面试

聊聊SpringBoot和传统的SSM的区别?SpringBoot是Spring的扩展,在Spring的基础上,简化了传统的SSM开发繁琐的配置;在部署上,SpringBoot内置了Tomcat,可以直接将项目打包成可执行的jar/war。说说你了解的Docker?