前言

本文档是 D瓜哥 阅读 Spring Boot 源码以及相关文档时的笔记。对学习内容做一些总结和提炼,分享出来也方便大家一起学习,共同进步。

友情支持

如果您觉得这个笔记对您有所帮助,看在D瓜哥码字的辛苦上,请友情支持一下,D瓜哥感激不尽,😜

支付宝

微信

有些打赏的朋友希望可以加个好友,欢迎关注D瓜哥的微信公众号,这样就可以通过公众号的回复直接给我发信息。

wx jikerizhi

公众号的微信号是: jikerizhi (“极客日志”全拼)因为众所周知的原因,有时图片加载不出来。如果图片加载不出来可以直接通过搜索微信号来查找我的公众号。

官网及版本库

本文档的版本库托管在 Github 上,另外单独发布。

“地瓜哥”博客网

https://www.diguage.com/ 。D瓜哥的个人博客。欢迎光临,不过,内容很杂乱,请见谅。不见谅,你来打我啊,😂😂

本文档官网

https://diguage.github.io/spring-boot/ 。为了方便阅读,这里展示了处理好的文档。阅读请点击这个网址。

本文档版本库

https://github.com/diguage/spring-boot 。由于组织方式的特殊性,坦白讲,不建议大家发 PR。有问题,欢迎发 Issue 讨论。

1. Spring Boot 重点

  1. 零配置

  2. 启动流程。重点利用了 Spring 的 ApplicationEvent 订阅/发布模式。

  3. 自动配置

  4. 运维

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 容器中。