深入springboot怎么启动tomcat

深入springboot怎么启动tomcat

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

深入springboot怎么启动tomcat_第1张图片

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

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

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

@EnableAutoConfiguration做了哪些事

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

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

深入springboot怎么启动tomcat_第2张图片
我们发现这里配置了一个类: ServletWebServerFactoryAutoConfiguration。
这个类是干嘛用的呢?
点进去一看!
不看不知道,一看吓一尿:
深入springboot怎么启动tomcat_第3张图片
根据上图的标识我们分三步来解释:

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

  2. @EnableConfigurationProperties({ServerProperties.class}),这个注解我在深入Springboot启动流程+自动配置原理.中有解释。那么呢,这里呢,就是去绑定我们application.properties中关于server的参数!
    例如:
    深入springboot怎么启动tomcat_第4张图片
    指定我们服务的端口、地址等。在SpringBoot启动的时候就会被从application.properties中读取到当前的ServletWebServerFactoryAutoConfiguration配置类中,进行web服务的初始化!

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

好!由于我们这里研究的是TomCat,我们点进EmbeddedTomcat看看!
深入springboot怎么启动tomcat_第5张图片
可以看到EmbeddedTomcat是一个由@Configuration修饰的静态内部配置类,向容器中注入了一个名叫TomcatServletWebServerFactory的对象(重点,后面会用到这个对象)。

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

  1. EmbeddedTomcat
  2. EmbeddedJetty
  3. EmbeddedUndertow

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

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

深入springboot怎么启动tomcat_第6张图片
我们发现通过new TomcatServletWebServerFactory(); 创建了一个TomcatServletWebServerFactory对象,然后构造方法设置了一系列参数。这里我们直接跳过,去看这个类中最重要的一个方法:getWebServer()
深入springboot怎么启动tomcat_第7张图片
这个方法我们后面会做解释,划重点!!!!!

小总结

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

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


Tomcat何时启动的呢?

这个时候就要回到我们的启动类上了:
深入springboot怎么启动tomcat_第8张图片
这个SpringApplication.run()方法我们之前一直没有系统介绍过,这里我简单介绍一下。
首先我们点进run方法
发现首先创建了一个SpringApplication对象。
然后继续调用到了一个同名的run方法头上。
好!
重点就是这个run方法了,它做了什么事情呢?
深入springboot怎么启动tomcat_第9张图片

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

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

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

我们进入refreshContext()方法看看:
深入springboot怎么启动tomcat_第10张图片
好,继续点:
在这里插入图片描述

继续:
在这里插入图片描述
发现是一个接口,我们看有那些实现类:

在这里插入图片描述
哇,有那么多实现类,到底调用的是哪个呢?
java多态的知识来了
这个时候我们去看看,这个方法是谁调用的?
在这里插入图片描述
是一个叫ConfigurableApplicationContext的类调用的,那么继续去寻找,我们就会发现:
在这里插入图片描述
我们要找的实现类是这个!

点进去!

深入springboot怎么启动tomcat_第11张图片

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

在这里插入图片描述
妈的,继续找实现类:
在这里插入图片描述
找到和web相关的实现类进去一看:
深入springboot怎么启动tomcat_第12张图片
来了来了
就是这个方法:
深入springboot怎么启动tomcat_第13张图片

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

调用了什么方法????

是不是getWebServer()!!

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

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

好了,最终的决战来了。

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

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

好!开始:

深入springboot怎么启动tomcat_第15张图片

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

继续:
在这里插入图片描述
new了一个TomcatWebServer对象(WebServer的子类),我们去看看它的构造方法:
深入springboot怎么启动tomcat_第16张图片
一顿非空判断加赋值,最后调用到了initialize()方法。 看到这个名字就知道,这是什么?

初始化

深入springboot怎么启动tomcat_第17张图片
Tomcat启动了。

小总结

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

最后由ServletWebServerFactory调用getWebServer()方法启动web容器。
深入springboot怎么启动tomcat_第18张图片



你的点赞收藏是我分享的动力,谢谢。

你可能感兴趣的:(Spring,spring,boot,tomcat,java,spring,后端)