前言
本文档是 D瓜哥 阅读 Spring Boot 源码以及相关文档时的笔记。对学习内容做一些总结和提炼,分享出来也方便大家一起学习,共同进步。
友情支持
如果您觉得这个笔记对您有所帮助,看在D瓜哥码字的辛苦上,请友情支持一下,D瓜哥感激不尽,😜
有些打赏的朋友希望可以加个好友,欢迎关注D瓜哥的微信公众号,这样就可以通过公众号的回复直接给我发信息。
公众号的微信号是: jikerizhi (“极客日志”全拼)。因为众所周知的原因,有时图片加载不出来。如果图片加载不出来可以直接通过搜索微信号来查找我的公众号。 |
官网及版本库
本文档的版本库托管在 Github 上,另外单独发布。
- “地瓜哥”博客网
-
https://www.diguage.com/ 。D瓜哥的个人博客。欢迎光临,不过,内容很杂乱,请见谅。不见谅,你来打我啊,😂😂
- 本文档官网
-
https://diguage.github.io/spring-boot/ 。为了方便阅读,这里展示了处理好的文档。阅读请点击这个网址。
- 本文档版本库
-
https://github.com/diguage/spring-boot 。由于组织方式的特殊性,坦白讲,不建议大家发 PR。有问题,欢迎发 Issue 讨论。
2. 启动流程
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
package com.diguage.truman;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* Truman 应用
*
* @author D瓜哥 · https://www.diguage.com
* @since 2021-08-04 08:50:57
*/
@Configuration
@EnableConfigurationProperties
@SpringBootApplication
public class TrumanApplication implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(TrumanApplication.class);
public static void main(String[] args) {
SpringApplication.run(TrumanApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
logger.info("Start……");
}
}
Failed to generate image: Could not load PlantUML. Either require 'asciidoctor-diagram-plantuml' or specify the location of the PlantUML JAR(s) using the 'DIAGRAM_PLANTUML_CLASSPATH' environment variable. Alternatively a PlantUML binary can be provided (plantuml-native in $PATH). @startuml header D瓜哥 · ""https://www.diguage.com"" title **Spring Boot 启动流程** actor Actor participant SpringApplication << (C,#ADD1B2) >> participant SpringFactoriesLoader << (C,#ADD1B2) >> participant SpringApplicationRunListener << (I,#AB9DE1) >> participant ApplicationContextInitializer << (I,#AB9DE1) >> participant ConfigurableApplicationContext << (I,#AB9DE1) >> participant Runner << (I,#AB9DE1) >> Actor -> SpringApplication: ""SpringApplication""\n创建 ""SpringApplication"" 实例 activate SpringApplication SpringApplication -> SpringFactoriesLoader: ""forDefaultResourceLocation""\n加载 ""BootstrapRegistryInitializer"" note right: 默认没有实现类 activate SpringFactoriesLoader ||| SpringApplication <- SpringFactoriesLoader deactivate SpringFactoriesLoader ||| SpringApplication -> SpringApplication: ""setInitializers""\n设置 ""ApplicationContextInitializer"" note left: 这里是拿下面 ""load"" \n方法的返回值去设置。 activate SpringApplication SpringApplication -> SpringFactoriesLoader: ""forDefaultResourceLocation""\n加载 ""ApplicationContextInitializer"" activate SpringFactoriesLoader ||| SpringApplication <- SpringFactoriesLoader deactivate SpringFactoriesLoader SpringApplication -> SpringApplication deactivate SpringApplication ||| SpringApplication -> SpringApplication: ""setListeners""\n设置 ""ApplicationListener"" note left: 这里是拿下面 ""load"" \n方法的返回值去设置。 activate SpringApplication SpringApplication -> SpringFactoriesLoader: ""forDefaultResourceLocation""\n加载 ""ApplicationListener"" activate SpringFactoriesLoader ||| SpringApplication <- SpringFactoriesLoader deactivate SpringFactoriesLoader SpringApplication -> SpringApplication deactivate SpringApplication ||| SpringApplication -> SpringApplication: ""deduceMainApplicationClass""\n确定 Main 类 activate SpringApplication ||| SpringApplication -> SpringApplication deactivate SpringApplication Actor <- SpringApplication: ""SpringApplication"" deactivate SpringApplication ==== Actor -> SpringApplication: ""run""\n启动 Spring Boot activate SpringApplication SpringApplication -> SpringApplication: ""getRunListeners""\n获取 ""SpringApplicationRunListeners"" note right: 将 ""SpringApplicationRunListener"" 集合\n封装到 ""SpringApplicationRunListeners"" 对\n象中,方便后续的遍历操作。 activate SpringApplication SpringApplication -> SpringFactoriesLoader: ""load""\n加载 ""SpringApplicationRunListener"" activate SpringFactoriesLoader ||| SpringApplication <- SpringFactoriesLoader deactivate SpringFactoriesLoader SpringApplication -> SpringApplication deactivate SpringApplication ||| loop 通知所有 ""SpringApplicationRunListener"" 监听器 SpringApplication -> SpringApplicationRunListener: ""starting"" note right #ff7d61: 只有一个实现类: ""EventPublishingRunListener""。\n该类其实有些“名不副实”,私以为叫 “Multicaster” 更合适。\n因为此类只发布事件,通过 ""SimpleApplicationEventMulticaster""\n类型的实例属性,将事件广播出去。\n而真正监听事件并做出相应的是 ""ApplicationListener"" 实例。这些实\n例从 ""SpringApplication"" 实例中获取,然后注册到上面的\n""SimpleApplicationEventMulticaster"" 实例内。 activate SpringApplicationRunListener ||| SpringApplication <- SpringApplicationRunListener deactivate SpringApplicationRunListener end ||| SpringApplication -> SpringApplication: ""getOrCreateEnvironment""\n创建 ""ConfigurableEnvironment"" 对象\n准备环境变量等相关配置信息 activate SpringApplication ||| SpringApplication -> SpringApplication deactivate SpringApplication ||| loop 通知所有 ""SpringApplicationRunListener"" 监听器 SpringApplication -> SpringApplicationRunListener: ""environmentPrepared"" activate SpringApplicationRunListener ||| SpringApplication <- SpringApplicationRunListener deactivate SpringApplicationRunListener end ||| SpringApplication -> SpringApplication: ""createApplicationContext""\n创建 ""ConfigurableApplicationContext"" 对象 activate SpringApplication ||| SpringApplication -> SpringApplication deactivate SpringApplication ||| SpringApplication -> SpringApplication: ""prepareContext""\n配置 ""ConfigurableApplicationContext"" activate SpringApplication ||| loop 调用所有 ""ApplicationContextInitializer"" SpringApplication -> ApplicationContextInitializer: ""initialize"" activate ApplicationContextInitializer note over SpringApplication,ApplicationContextInitializer: 调用 ""ApplicationContextInitializer"",对 ""ConfigurableApplicationContext"" 进行初始化。 SpringApplication <- ApplicationContextInitializer deactivate ApplicationContextInitializer end ||| loop 通知所有 ""SpringApplicationRunListener"" 监听器 SpringApplication -> SpringApplicationRunListener: ""contextPrepared"" activate SpringApplicationRunListener ||| SpringApplication <- SpringApplicationRunListener deactivate SpringApplicationRunListener end note over SpringApplication,SpringApplicationRunListener:在此配置 ""ConfigurableApplicationContext"",\n主要是注册一些 Bean,两个 ""BeanFactoryPostProcessor"": \n#""LazyInitializationBeanFactoryPostProcessor""\n#""PropertySourceOrderingBeanFactoryPostProcessor"" ||| loop 通知所有 ""SpringApplicationRunListener"" 监听器 SpringApplication -> SpringApplicationRunListener: ""contextLoaded"" activate SpringApplicationRunListener ||| SpringApplication <- SpringApplicationRunListener deactivate SpringApplicationRunListener end ||| SpringApplication -> SpringApplication deactivate SpringApplication ||| SpringApplication -> SpringApplication: ""refreshContext""\n""ConfigurableApplicationContext"" 刷新 activate SpringApplication #FF33FF SpringApplication -> ConfigurableApplicationContext: ""refresh"" note left: !!!重点 !!!\nSpring 容器开始 refresh activate ConfigurableApplicationContext ||| SpringApplication <- ConfigurableApplicationContext deactivate ConfigurableApplicationContext SpringApplication -> SpringApplication deactivate SpringApplication ||| loop 通知所有 ""SpringApplicationRunListener"" 监听器 SpringApplication -> SpringApplicationRunListener: ""started"" note over SpringApplication,SpringApplicationRunListener: 给所有实现 ""ApplicationContextAware"" 接口的 ""ApplicationListener"" 实例设置容器对象。\n然后将所有的 ""ApplicationListener"" 实例,都添加到 Spring 容器中。后续通知通过容器来发布。 activate SpringApplicationRunListener SpringApplication <- SpringApplicationRunListener deactivate SpringApplicationRunListener end ||| loop 依次调用所有 ""Runner"" SpringApplication -> Runner: ""run"" note over SpringApplication,Runner:这里依次调用两种 ""Runner""\n#""ApplicationRunner""\n#""CommandLineRunner"" activate Runner SpringApplication <- Runner deactivate Runner end ||| alt #D5E8D4 启动成功 loop 通知所有 ""SpringApplicationRunListener"" 监听器 SpringApplication -> SpringApplicationRunListener: ""ready"" activate SpringApplicationRunListener ||| SpringApplication <- SpringApplicationRunListener deactivate SpringApplicationRunListener end else #F8CECC 启动失败 loop 通知所有 ""SpringApplicationRunListener"" 监听器 SpringApplication -> SpringApplicationRunListener: ""failed"" activate SpringApplicationRunListener ||| SpringApplication <- SpringApplicationRunListener deactivate SpringApplicationRunListener end end Actor <- SpringApplication: ""ApplicationContext"" deactivate SpringApplication footer D瓜哥 · ""https://www.diguage.com"" · 出品 @enduml
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
package com.diguage.truman;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
/**
* MockBean 测试
*
* @author D瓜哥 · https://www.diguage.com
* @since 2021-08-04 08:50:57
*/
@Slf4j
@SpringBootTest(classes = TrumanApplication.class)
@ExtendWith(SpringExtension.class)
public class MockBeanTest {
@MockitoBean
private Runnable run;
@Test
public void test() {
assertThat(this.run).isNotNull();
assertThat(this.run).isInstanceOf(Runnable.class);
// org.mockito.codegen.Runnable$MockitoMock$XXXXX
// 从这里可以看出,MockBean 是由 Mockito 创建的 Mock。
// 底层是 Byte Buddy 利用字节码编辑技术动态生成的类。
log.info("class={}", this.run.getClass().getName());
}
}
3. .imports
扩展机制
Spring Boot 3 开始,支持以 .imports
为文件扩展名的扩展机制。扩展机制的入口在 org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.selectImports
。扩展机制的核心是以相应的文件名在类路径上去查找资源,找到后加载文件中包含的类。加载文件的过程如下:
ImportCandidates.load
1
Unresolved directive in index.adoc - include::{core_src_dir}/context/annotation/ImportCandidates.java[tag=load]
解析到这些类之后,最后是通过 Spring 的 @Import
扩展机制,将这些类作为 Bean 加入到 Spring 容器中。