深入springboot怎么启动tomcat

  • @EnableAutoConfiguration做了哪些事
    • 小总结
  • Tomcat何时启动的呢?
    • 小总结

这是中高级工程师面试中常问的问题。
知道现在有多卷了吧!
我记得我刚找工作那会儿,我只要8000的工资,面试官都要问这个问题。我真TM的醉了!

关于SpringBoot自动配置流程请看:深入Springboot启动流程+自动配置原理.

如果你对基本的启动原理有大致的了解,那么可以继续阅读此篇文章。否则请先阅读深入Springboot启动流程+自动配置原理.。

@EnableAutoConfiguration做了哪些事

我们知道,因为@EnableAutoConfiguration注解的存在,SpringBoot项目启动的时候,会去找到依赖包中META-INF/spring.factories文件,将文件中的自动配置类找出来,加载进内存!

那么和Tomcat相关的配置类,也存在于spring.factories之中:


我们发现这里配置了一个类: ServletWebServerFactoryAutoConfiguration。
这个类是干嘛用的呢?
点进去一看!
不看不知道,一看吓一尿:

根据上图的标识我们分三步来解释:

  1. @ConditionalOnWebApplication( type = Type.SERVLET):条件判断,如果当前的项目的类型为Servlet我才继续运行当前的ServletWebServerFactoryAutoConfiguration配置类,这里判断通过。

  2. @EnableConfigurationProperties({ServerProperties.class}),这个注解我在深入Springboot启动流程+自动配置原理.中有解释。那么呢,这里呢,就是去绑定我们application.properties中关于server的参数!
    例如:

    指定我们服务的端口、地址等。在SpringBoot启动的时候就会被从application.properties中读取到当前的ServletWebServerFactoryAutoConfiguration配置类中,进行web服务的初始化!

  3. @Import(EmbeddedTomcat.class、 EmbeddedJetty.class): 这是什么? Tomcat?Jetty?并且通过@Import注解注入容器了!聪明的朋友猜到了,Tomcat/Jetty服务就是在这里进行初始化的。

好!由于我们这里研究的是TomCat,我们点进EmbeddedTomcat看看!

可以看到EmbeddedTomcat是一个由@Configuration修饰的静态内部配置类,向容器中注入了一个名叫TomcatServletWebServerFactory的对象(重点,后面会用到这个对象)。

这个时候有同学就有疑问了,在ServletWebServerFactoryAutoConfiguration类中通过@Import导入了三个对象:

  1. EmbeddedTomcat
  2. EmbeddedJetty
  3. EmbeddedUndertow

那这里为什么只加载了EmbeddedTomcat呢? 因为我们的@ConditionalOnClass条件注解,我们的依赖中没有Jetty和Undertow相关的类,因此EmbeddedJetty和EmbeddedUndertow不会加载。只会加载EmbeddedTomcat!

所以现在呢,重点来到了TomcatServletWebServerFactory这个对象上,我们进去看看。


我们发现通过new TomcatServletWebServerFactory(); 创建了一个TomcatServletWebServerFactory对象,然后构造方法设置了一系列参数。这里我们直接跳过,去看这个类中最重要的一个方法:getWebServer()

这个方法我们后面会做解释,划重点!!!!!

小总结

那么@EnableAutoConfiguration对于Tomcat的工作现在就做完了,做了什么事情呢?

向Spring容器中注入了一个初始化后,名叫TomcatServletWebServerFactory的对象。该对象继承自ServletWebServerFactory接口(记住)。我们现在可以认为TomCat服务准备好了,等待启动。那什么时候启动的呢?


Tomcat何时启动的呢?

这个时候就要回到我们的启动类上了:

这个SpringApplication.run()方法我们之前一直没有系统介绍过,这里我简单介绍一下。
首先我们点进run方法
发现首先创建了一个SpringApplication对象。
然后继续调用到了一个同名的run方法头上。
好!
重点就是这个run方法了,它做了什么事情呢?

我这里借鉴一下百度百科上面的解释:

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);//处理需要忽略的BeanconfigureIgnoreBeanInfo(environment);//打印bannerBanner 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实例化工作(告诉相关人员,SpringBoot要启动了,你们把自己该初始化的初始化了)
  2. 构造容器环境,简而言之就是加载系统变量,环境变量,配置文件
  3. 创建容器
  4. 实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
  5. 准备容器
  6. 刷新容器 :refreshContext(context)
  7. 刷新容器后的扩展接口

由于我们这里专门讨论TomCat的启动,所以我们其他的就不多去研究,直接找到最重要的地方:
第6步:刷新容器 ,这一步是整个SpringBoot非常重要的一步,IOC等等核心都是通过这一步来实现的。

我们进入refreshContext()方法看看:

好,继续点:

继续:

发现是一个接口,我们看有那些实现类:


哇,有那么多实现类,到底调用的是哪个呢?
java多态的知识来了
这个时候我们去看看,这个方法是谁调用的?

是一个叫ConfigurableApplicationContext的类调用的,那么继续去寻找,我们就会发现:

我们要找的实现类是这个!

点进去!

代码很长,我们直接去到onRefresh() 方法。
点进去:


妈的,继续找实现类:

找到和web相关的实现类进去一看:

来了来了
就是这个方法:

  1. 获取到容器中已经存在的ServletWebServerFactory对象(是不是有点熟悉?回到第一节结尾处去看看!),然后调用了什么方法?

调用了什么方法????

是不是getWebServer()!!

我们看看ServletWebServerFactory的子类(由于我这里的按理项目有两个Spring版本,不管它,正常情况下面的实现类,一种只有一个):
其中是不是有TomcatServletWebServerFactory
而这个TomcatServletWebServerFactory是不是在@EnableAutoConfiguration环节创建好的???

好!!! 现在程序拿到了我们的TomcatServletWebServerFactory,并调用了其中的getWebServer()方法!!!!

好了,最终的决战来了。

getWebServer()方法到底做了什么??

我们到目前为止还没看到TomCat启动的任何蛛丝马迹,getWebServer()方法是我们最后的救命稻草了。

好!开始:

  1. 创建了一个Tomcat对象,然后一系列赋值。
  2. 调用了一个叫getTomcatWebServer()的方法。最终返回一个WebServer对象

继续:

new了一个TomcatWebServer对象(WebServer的子类),我们去看看它的构造方法:

一顿非空判断加赋值,最后调用到了initialize()方法。 看到这个名字就知道,这是什么?

初始化


Tomcat启动了。

小总结

在 SpringApplication.run()中,刷新容器的时候,程序会去找到在@EnableAutoConfiguration阶段创建好的ServletWebServerFactory,它的实现类可能是TomCat可能是Jetty,根据我们项目所引入的依赖自动实例化。

最后由ServletWebServerFactory调用getWebServer()方法启动web容器。



深入springboot怎么启动tomcat相关推荐

  1. SpringBoot内置tomcat启动原理

    前言 不得不说SpringBoot的开发者是在为大众程序猿谋福利,把大家都惯成了懒汉,xml不配置了,连tomcat也懒的配置了,典型的一键启动系统,那么tomcat在springboot是怎么启动的 ...

  2. Springboot内嵌tomcat

    Springboot内嵌tomcat 前言 一.依赖引入starter-web 二.源码截图 tomcat 如何内嵌 三.springboot 如何启动tomcat 前言 一.依赖引入starter- ...

  3. SpringBoot中的Tomcat是如何启动的

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-bo ...

  4. tomcat start 无法启动_解密Springboot内嵌Tomcat

    Springboot简介 相信大多数开发者对Springboot比较熟悉了,它能够快速地创建一个spring应用,能够完全摒弃XML的配置方式,并且内嵌了Tomcat.Jetty这样的Servlet容 ...

  5. SpringBoot内置Tomcat启动不了的原因

    SpringBoot内置Tomcat启动不了的原因: 1.需要加入spring-boot-starter-web依赖 [web中集成了tomcat.dispatcherServlet.xml-] &l ...

  6. springboot内嵌Tomcat启动失败

    问题描述 开发需求期间引入友军的二方包,导致服务启动失败,失败日志如下 java.lang.reflect.InvocationTargetExceptionat sun.reflect.Native ...

  7. SpringBoot启动Tomcat原理与嵌入式Tomcat实践

    导读 作为一个开发,使用Spring Boot 时,和传统的Tomcat 部署相比,我们只需要关注业务的开发,项目的启动和部署变的十分简单, 那么它背后是怎么实现的, 隐藏着什么? 本文先从一个嵌入式 ...

  8. idea springboot配置外置tomcat好处

    idea springboot配置外置tomcat好处: 1.原先用内置的tomcat,改动一点点java代码,热部署时会自动重启(可以重控制台中重到)---------慢 2.原先用内置的tomca ...

  9. SpringBoot内置tomcat出现error:An incompatible version [1.1.32] of the APR based Apache Tomcat Native lib

    SpringBoot内置tomcat出现error:An incompatible version [1.1.32] of the APR based Apache Tomcat Native lib ...

最新文章

  1. Android开发实践:在任意目录执行NDK编译
  2. python电脑下载网址-python下载文件文件到本地电脑(基于requests)
  3. 【spring-boot】restfull api 返回值中,去掉 null 值
  4. 18岁初中毕业学Java_刚满十八 初中毕业 java自学完了 没学历 该怎么办?
  5. (原创总结) Quartus II 的在线调试方法
  6. HDOJ---2546 饭卡[DP01背包问题]
  7. php验证码只有图片没有文字_有没有免费好用的图片文字识别工具?在线就能使用超准确...
  8. launchpad乐器_PreSonus 发布 ATOM 打击垫控制器(视频)
  9. Canvas实例之鼠标移动特效(彩色小球)
  10. Migration——迁移
  11. 美国3D理发师可剪出球星脸发型
  12. 湖北科目三驾考经验总结
  13. unity如何插入图片_unity 图片导入及其使用方法
  14. spring tx:advice 和 aop:config 配置事务 1
  15. Beta冲刺总结随笔
  16. java毕业设计车牌信息管理系统Mybatis+系统+数据库+调试部署
  17. requests使用splash
  18. Opencv 分水岭算法 watershed的图像分割
  19. 抖音开发者工具配置抖音小游戏为横屏显示的方法
  20. 汉字转拼音的使用手册

热门文章

  1. TCP 糊涂窗口综合症(silly window syndrome)与 rate-based 流控
  2. 个人的生活经历和实习经历
  3. caffe与卷积神经网络
  4. QUICK PCB抄板教程(无网络编号)
  5. 【转】Voip 知识
  6. starting tomcat v7.0 server at localhost has encountered a problem错误解法方法
  7. [Swift]LeetCode60. 第k个排列 | Permutation Sequence
  8. 宏观调控绝不是微观控制
  9. 在线检查英文语法神器
  10. 免外围电路ESP32/ESP8266系列单片机串口一键下载方案