龙空技术网

SpringBoot内置tomcat启动过程及原理

京东云开发者 1738

前言:

现时小伙伴们对“tomcat在哪里启动”大概比较关怀,同学们都需要剖析一些“tomcat在哪里启动”的相关知识。那么小编在网摘上收集了一些有关“tomcat在哪里启动””的相关文章,希望小伙伴们能喜欢,各位老铁们一起来学习一下吧!

作者:李岩科1 背景

SpringBoot是一个框架,一种全新的编程规范,他的产生简化了框架的使用,同时也提供了很多便捷的功能,比如内置tomcat就是其中一项,他让我们省去了搭建tomcat容器,生成war,部署,启动tomcat。因为内置了启动容器,应用程序可以直接通过 Maven 命令将项目编译成可执行的 jar 包,通过 java -jar 命令直接启动,不需要再像以前一样,打包成 War 包,然后部署在 Tomcat 中。那么内置tomcat是如何实现的呢

2 tomcat启动过程及原理2.1 下载一个springboot项目

在这里下载一个项目 也可以在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类型,实例化 AnnotationConfigServletWebServerApplicationContextDEFAULT_REACTIVE_WEB_CONTEXT_CLASS:响应式Web类型,实例化 AnnotationConfigReactiveWebServerApplicationContextDEFAULT_CONTEXT_CLASS:非Web类型,实例化 AnnotationConfigApplicationContext2.4 创建完应用上下文之后,我们在看刷新上下文方法

一步步通过断点点击方法进去查看,我们看到很熟悉代码spring的相关代码。

@Overridepublic 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的相关设置和参数

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

标签: #tomcat在哪里启动