本文主要分析 SpringBoot 的启动过程。
SpringBoot的版本为:2.1.0 release,最新版本。
一.时序图
还是老套路,先把分析过程的时序图摆出来:时序图-SpringBoot2.10启动分析
二.源码分析
首先从我们的一个SpringBoot Demo开始,这里使用
网站生成的starter开始的:经过SpringApplication多个重载的构造方法,最后到达:
看一眼,WebApplicationType#deduceFromClasspath ,deduce意为推断,即根据classpath下的内容推断出应用的类型。实现是通过ClassUtils#isPresent来尝试加载代表不同应用类型特征的Class文件:
SpringApplication#getSpringFactoriesInstances,从类路径下 META-INF/spring.factories 下加载 SpringFactory 实例,类似的操作在 Dubbo SPI中也有:
SpringFactoriesLoader#loadFactoryNames,加载工厂名字:
继续捉迷藏,到了 SpringFactoriesLoader#loadSpringFactories:下面的内容就是找到所有classpath下的 spring.factories 文件,读取里面的内容,放到缓存中,此处和Dubbo SPI中ExtensionLoader#loadDirectory几乎是一模一样,可以参考我写过的
里面的注释。我们也来看一下上面读取的文件 spring.factories 的内容,大概长这个样子:
是时候跳出来了,回到主线,返回实例化对象后,到了 SpringApplication#deduceMainApplicationClass,获取程序当前运行堆栈,看现在运行的是哪个类的 main 方法,然后保存到上下文:
至此,SpringApplication的构造函数的分析完成,后面我们继续分析SpringApplication的run()方法中做了哪些操作。
SpringBoot的版本为:2.1.0 release,最新版本。
一.时序图
一样的,我们先把时序图贴上来,方便理解:
二.源码分析
回顾一下,前面我们分析到了下面这步:
SpringApplication#run方法的内容较多,准备刷屏了:
上面从大体上介绍了SpringApplication在run()中做的事情,下面详细分析每步具体的操作。
SpringApplication#getRunListeners,获取所有启动监听器,一样的套路,通过SPI机制,通过工厂创建SpringApplicationRunListener的实例:
从 spring.factories 里面找到如下内容:也就是最后拿到SpringApplicationRunListener的实例是EventPublishingRunListener的对象。
SpringApplication#configureHeadlessProperty,设置系统属性 "java.awt.headless" 。headless是系统的一种配置模式,在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式,也就是告诉服务器,没有这些硬件设施,当需要这是设备信息的时候,别慌,我可以使用awt组件通过计算模拟出这些外设的特性。
SpringApplication#prepareEnvironment,准备应用上下文 environment 对象,像设置当前使用的配置文件profile,就在该方法内完成:
SpringApplication#configureEnvironment,添加 ConversionService 转换器,保存启动参数,即最外面main的入参args数组:
SpringApplication#configureProfiles ,设置当前运行环境的配置文件,会去查看"spring.profiles.active"中指定的是什么:
不小心有陷入进去了,再次回到run()。
接着看后面是获取Banner。根据环境找到Banner,默认找classpath下面的 banner.txt,Gitee上面很多管理系统打印出各种自定义名称。实现很简单,只要你提前制作好banner.txt,放到resources下面,启动的时候,就会加载你的,覆盖原始的Banner信息。这部分代码比较简单,只要跟进去看下就能看明白,考虑篇幅问题,省略过了。分享一个
。如果需要关闭Banner输出,在App里面调用:
SpringApplication.setBannerMode(Banner.Mode.OFF);// 关掉Banner
接下来重点看一下 SpringApplication#prepareContext,分析SpringBoot如何准备上下文的:
稳住,快启动完了。接着看SpringApplication#refreshContext,完成刷新上下文的操作:
然后是到了SpringApplication#afterRefresh,这是一个模板方法,父类不提供实现,留给子类发挥想象实现,在context刷新好之后需要做的事情可以在此方法实现中完成。
最后就是 stopWatch.stop() 停止计时,打印启动耗时信息,回调监听器的started(ctx)方法,返回上下文context。
至此,SpringBoot启动过程分析完成。