广告位联系
返回顶部
分享到

SpringBoot内置Tomcat启动方式

java 来源:互联网 作者:佚名 发布时间:2024-12-11 22:44:02 人浏览
摘要

一、Tomcat相关配置类如何加载的? 在springboot项目中,我们只需要引入spring-boot-starter-web依赖,启动服务成功,我们一个web服务就搭建好了,没有明显的看到tomcat。 其实打开spring-boot-starter-web依

一、Tomcat相关配置类如何加载的?

在springboot项目中,我们只需要引入spring-boot-starter-web依赖,启动服务成功,我们一个web服务就搭建好了,没有明显的看到tomcat。

其实打开spring-boot-starter-web依赖,我们可以看到:依赖了tomcat。

1.进入Springboot启动类

我们加入Springboot最核心的注解@SpringBootApplication,源码如下图:重点看注解@EnableAutoConfiguration,

2.进入注解@EnableAutoConfiguration

如下图:该注解通过@Import注解导入了AutoConfigurationImportSelector类。

其实这个类,就是导入通过加载配置文件,加载了很多工厂方法的配置类。

3.进入AutoConfigurationImportSelector类

首先调用selectImport()方法,在该方法中调用了 getAutoConfigurationEntry()方法,在之中又调用了getCandidateConfigurations()方法, getCandidateConfigurations()方法就去META-INF/spring.factory配置文件中加载相关配置类。

详细讲解如下:也就是下图的,方法1调用方法2,方法2调用方法3:

到了这里加载了 META-INF/spring.factories文件:

4.我们看到

加载了ServletWebServerFactoryAutoConfiguration这个配置类,web工厂配置类。

1

2

3

4

5

6

7

8

9

10

11

12

@Configuration(proxyBeanMethods = false)

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)

@ConditionalOnClass(ServletRequest.class)

@ConditionalOnWebApplication(type = Type.SERVLET)

@EnableConfigurationProperties(ServerProperties.class)

@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,

        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,

        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,

        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })

public class ServletWebServerFactoryAutoConfiguration {

  ...

}

从这个配置工厂类,我们看出通过@Import注解加载了tomcat,jetty,undertow三个web服务器的配置类。

由于没有导入jetty和undertow的相关jar包,这两个类实例的不会真正的加载。

5.进入EmbeddedTomcat类

创建了TomcatServletWebServerFactory类的对象。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

@Configuration(proxyBeanMethods = false)

class ServletWebServerFactoryConfiguration {

 

    @Configuration(proxyBeanMethods = false)

    @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })

    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

    static class EmbeddedTomcat {

 

        @Bean

        TomcatServletWebServerFactory tomcatServletWebServerFactory(

                ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,

                ObjectProvider<TomcatContextCustomizer> contextCustomizers,

                ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {

            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

            factory.getTomcatConnectorCustomizers()

                    .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));

            factory.getTomcatContextCustomizers()

                    .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));

            factory.getTomcatProtocolHandlerCustomizers()

                    .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));

            return factory;

        }

    }

6.进入TomcatServletWebServerFactory类

关注getWebServer()方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

@Override

public WebServer getWebServer(ServletContextInitializer... initializers) {

    if (this.disableMBeanRegistry) {

        Registry.disableRegistry();

    }

    //实例化一个Tomcat

    Tomcat tomcat = new Tomcat();

    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");

    //设置Tomcat的工作临时目录

    tomcat.setBaseDir(baseDir.getAbsolutePath());

    //默认使用Http11NioProtocal实例化Connector

    Connector connector = new Connector(this.protocol);

    connector.setThrowOnFailure(true);

    //给Service添加Connector

    tomcat.getService().addConnector(connector);

    customizeConnector(connector);

    tomcat.setConnector(connector);

    //关闭热部署

    tomcat.getHost().setAutoDeploy(false);

    //配置Engine

    configureEngine(tomcat.getEngine());

    for (Connector additionalConnector : this.additionalTomcatConnectors) {

        tomcat.getService().addConnector(additionalConnector);

    }

    prepareContext(tomcat.getHost(), initializers);

    // 实例化TomcatWebServer时会将DispatcherServlet以及一些Filter添加到Tomcat中

    return getTomcatWebServer(tomcat);

}

getWebServer()方法在当前类,调用了getTomcatWebServer()方法,其实又new TomcatWebServer()对象:

1

2

3

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {

    return new TomcatWebServer(tomcat, getPort() >= 0);

}

7.进入TomcatWebServer类

这个类才是真正的做tomcat启动的类:

(1)构造方法:调用了initialize()方法

1

2

3

4

5

6

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {

        Assert.notNull(tomcat, "Tomcat Server must not be null");

        this.tomcat = tomcat;

        this.autoStart = autoStart;

        initialize();

    }

(2)进入initialize()方法,这个方法:this.tomcat.start(),启动tomcat容器了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

private void initialize() throws WebServerException {

        logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));

        synchronized (this.monitor) {

            try {

                addInstanceIdToEngineName();

 

                Context context = findContext();

                context.addLifecycleListener((event) -> {

                    if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {

                        // Remove service connectors so that protocol binding doesn't

                        // happen when the service is started.

                        removeServiceConnectors();

                    }

                });

 

                // Tomcat在这里启动了

                this.tomcat.start();

 

                // We can re-throw failure exception directly in the main thread

                rethrowDeferredStartupExceptions();

 

                try {

                    ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());

                }

                catch (NamingException ex) {

                    // Naming is not enabled. Continue

                }

 

                // Unlike Jetty, all Tomcat threads are daemon threads. We create a

                // blocking non-daemon to stop immediate shutdown

                startDaemonAwaitThread();

            }

            catch (Exception ex) {

                stopSilently();

                destroySilently();

                throw new WebServerException("Unable to start embedded Tomcat", ex);

            }

        }

    }

二、getWebServer()的调用分析,也就是tomcat何时启动的

上面分析了tomcat的配置到启动的方法,我们现在来分析,tomcat是何时启动的。

1.首先进入SpringBoot启动类的run方法

1

2

3

public static void main(String[] args) {

    SpringApplication.run(SpringBootMytestApplication.class, args);

}

最终调用了本类的一个同名方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

public ConfigurableApplicationContext run(String... args) {

        //记录程序运行时间

        StopWatch stopWatch = new StopWatch();

        stopWatch.start();

        // ConfigurableApplicationContext Spring 的上下文

        ConfigurableApplicationContext context = null;

        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

        configureHeadlessProperty();

        //【1、获取并启动监听器】

        SpringApplicationRunListeners listeners = getRunListeners(args);

        listeners.starting();

        try {

            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

            //【2、构造应用上下文环境】

            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

            //处理需要忽略的Bean

            configureIgnoreBeanInfo(environment);

            //打印banner

            Banner printedBanner = printBanner(environment);

            ///【3、初始化应用上下文】

            context = createApplicationContext();

            //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误

            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,

                    new Class[] { ConfigurableApplicationContext.class }, context);

            //【4、刷新应用上下文前的准备阶段】

            prepareContext(context, environment, listeners, applicationArguments, printedBanner);

            //【5、刷新应用上下文】

            refreshContext(context);

            //【6、刷新应用上下文后的扩展接口】

            afterRefresh(context, applicationArguments);

            //时间记录停止

            stopWatch.stop();

            if (this.logStartupInfo) {

                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);

            }

            //发布容器启动完成事件

            listeners.started(context);

            callRunners(context, applicationArguments);

        }

        catch (Throwable ex) {

            handleRunFailure(context, ex, exceptionReporters, listeners);

            throw new IllegalStateException(ex);

        }

 

        try {

            listeners.running(context);

        }

        catch (Throwable ex) {

            handleRunFailure(context, ex, exceptionReporters, null);

            throw new IllegalStateException(ex);

        }

        return context;

    }

这个方法大概做了以下几件事:

  • 1)获取并启动监听器 通过加载META-INF/spring.factories 完成了 SpringApplicationRunListener实例化工作
  • 2)构造容器环境,简而言之就是加载系统变量,环境变量,配置文件
  • 3)创建容器
  • 4)实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
  • 5)准备容器
  • 6) 刷新容器
  • 7)刷新容器后的扩展接口

2.那么内置tomcat启动源码

就是隐藏在上面第六步:refreshContext方法里面,该方法最终会调 用到AbstractApplicationContext类的refresh()方法,进入refreshContext()方法,如图:

1

2

3

4

5

6

7

8

9

10

11

private void refreshContext(ConfigurableApplicationContext context) {

    refresh(context);

    if (this.registerShutdownHook) {

        try {

            context.registerShutdownHook();

        }

        catch (AccessControlException ex) {

            // Not allowed in some environments.

        }

    }

}

refreshContext()调用了refresh()方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

public void refresh() throws BeansException, IllegalStateException {

        synchronized(this.startupShutdownMonitor) {

            this.prepareRefresh();

            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

            this.prepareBeanFactory(beanFactory);

 

            try {

                this.postProcessBeanFactory(beanFactory);

                this.invokeBeanFactoryPostProcessors(beanFactory);

                this.registerBeanPostProcessors(beanFactory);

                this.initMessageSource();

                this.initApplicationEventMulticaster();

                this.onRefresh();

                this.registerListeners();

                this.finishBeanFactoryInitialization(beanFactory);

                this.finishRefresh();

            } catch (BeansException var9) {

                if (this.logger.isWarnEnabled()) {

                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);

                }

 

                this.destroyBeans();

                this.cancelRefresh(var9);

                throw var9;

            } finally {

                this.resetCommonCaches();

            }

 

        }

    }

refresh()方法调用了this.onRefresh():

1

2

3

4

5

6

7

8

9

10

11

@Override

protected void onRefresh() {

    super.onRefresh();

    try {

        //核心方法:会获取嵌入式的Servlet容器工厂,并通过工厂来获取Servlet容器

        createWebServer();

    }

    catch (Throwable ex) {

        throw new ApplicationContextException("Unable to start web server", ex);

    }

}

如下面的代码:createWebServer() 方法调用了一个factory.getWebServer()。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

private void createWebServer() {

    WebServer webServer = this.webServer;

    ServletContext servletContext = getServletContext();

    if (webServer == null && servletContext == null) {

        //先获取嵌入式Servlet容器工厂

        ServletWebServerFactory factory = getWebServerFactory();

        this.webServer = factory.getWebServer(getSelfInitializer());

    }

    else if (servletContext != null) {

        try {

            getSelfInitializer().onStartup(servletContext);

        }

        catch (ServletException ex) {

            throw new ApplicationContextException("Cannot initialize servlet context", ex);

        }

    }

    initPropertySources();

}

到了这里getWebServer()方法,下一步就是创建TomcatWebServer对象,创建该对象,就在构造方法启动了Tomcat。详细代码在第一部分有。

总结

tomcat启动流程


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • Java-URLDecoder、URLEncoder使用及说明介绍

    Java-URLDecoder、URLEncoder使用及说明介绍
    前言 Java中的URLDecoder和URLEncoder是用于对URL进行编码和解码的类。 URL编码是将URL中的特殊字符转换成特定的格式,以便于在URL中传递参数。
  • SpringBoot内置Tomcat启动方式

    SpringBoot内置Tomcat启动方式
    一、Tomcat相关配置类如何加载的? 在springboot项目中,我们只需要引入spring-boot-starter-web依赖,启动服务成功,我们一个web服务就搭建好了,
  • Springboot接收Get参数实践过程

    Springboot接收Get参数实践过程
    一、参数直接在路径中 1.假设请求地址是如下这种 RESTful 风格 hangge 这个参数值直接放在路径里面: http://localhost:8080/helloworld/张三 1 2 3 4 5
  • MyBatis中的N+1问题的解决方法
    N+1 问题是指在进行一对多查询时,应用程序首先执行一条查询语句获取结果集(即 +1),然后针对每一条结果,再执行 N 条额外的查询语句
  • MyBatis中 #{} 和 ${} 的区别介绍
    在MyBatis中,#{}和${}是两种常见的占位符,它们的作用和使用场景有所不同。理解它们的区别对于正确使用MyBatis非常重要。 在Mybatis面试中常
  • MyBatis实现CRUD的代码

    MyBatis实现CRUD的代码
    准备工作 创建module(Maven的普通Java模块):mybatis-002-crud pom.xml 打包方式jar 依赖: mybatis依赖 mysql驱动依赖 junit依赖 logback依赖 mybatis-config
  • MyBatis中if标签的基本使用

    MyBatis中if标签的基本使用
    在MyBatis框架中,if标签用于在构建SQL语句时,根据参数条件判断的结果,动态地选择加入或不加where条件中。 一 常见使用 在使用MyBatis处理
  • Java中的字节流和字符流介绍
    Java 中的输入输出(I/O)流主要分为字节流和字符流。这两类流为开发者提供了高效的文件读写方式,也解决了不同编码格式下的字符处理问
  • Java中缓冲流的使用与性能提升(让文件操作更高效
    在Java的I/O操作中,文件读写是常见且频繁的任务。特别是对于大文件或需要频繁访问文件的程序,如何提升I/O性能成为了一个重要的问题。
  • Java中如何自定义一个类加载器加载自己指定的类
    在 Java 中,类加载器(ClassLoader)负责把字节码文件(.class 文件)加载到 JVM 中,Java 的类加载机制给我们提供了高度的灵活性。通常情况下
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计