# 前置知识

# Why Spring Boot(简化了什么)

official documents (opens new window)

While the Spring framework focuses on providing flexibility to you, Spring Boot aims to shorten the code length and provide you with the easiest way to develop a web application. With annotation configuration and default codes, Spring Boot shortens the time involved in developing an application. (opens new window)

自动配置、起步依赖、Actuator、命令行界面(CLI) 是Spring Boot最重要的4大核心特性

Servlet->EJB->Struts->SpringMVC->SpringBoot

# 核心特性概览

  • 起步依赖(Starter)
  • 自动配置(一句话概述)
  • 内嵌服务器
  • 外部化配置
  • Actuator & DevTools

# SpringApplication启动流程

Springboot Source Code

Spring Boot应用启动流程:

┌─────────────────────────────────────────────────┐
│               1. JVM启动阶段                    │
├─────────────────────────────────────────────────┤
│ 加载JVM → 类加载器初始化 → 字节码验证            │
└───────────────────────┬─────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────┐
│          2. SpringApplication.run()             │
├─────────────────────────────────────────────────┤
│ 设置无头模式 → 启动监听器 → 发布启动事件          │
└───────────────────────┬─────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────┐
│          3. 环境准备阶段                        │
├─────────────────────────────────────────────────┤
│ 创建应用参数 → 准备环境 → 加载配置 → 打印Banner  │
└───────────────────────┬─────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────┐
│          4. 应用上下文创建                      │
├─────────────────────────────────────────────────┤
│ 创建空容器实例 → 初始化异常报告器                 │
└───────────────────────┬─────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────┐
│          5. 上下文预处理                        │
├─────────────────────────────────────────────────┤
│ 设置环境 → 执行ApplicationContextInitializer     │
│ → 加载Bean定义源                                │
└───────────────────────┬─────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────┐
│          6. ApplicationContext.refresh()        │
├─────────────────────────────────────────────────┤
│ 这是Spring容器的核心启动方法,包含所有子阶段       │
└───────────────────────┬─────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────┐
│          7. 嵌套子阶段(按顺序执行)             │
├─────────────────────────────────────────────────┤
│ 7.1 prepareRefresh()                            │
│ 7.2 obtainFreshBeanFactory()                    │
│ 7.3 prepareBeanFactory()                        │
│ 7.4 postProcessBeanFactory()                    │
│ 7.5 invokeBeanFactoryPostProcessors() ← 重要!  │
│     ├── 扫描@Component注解类                    │
│     ├── 创建BeanDefinition                     │
│     ├── 注册到BeanFactory                      │
│     └── 处理@AutoConfiguration                 │
│ 7.6 registerBeanPostProcessors()                │
│ 7.7 initMessageSource()                         │
│ 7.8 initApplicationEventMulticaster()           │
│ 7.9 onRefresh() ← 启动内嵌服务器!              │
│ 7.10 registerListeners()                        │
│ 7.11 finishBeanFactoryInitialization() ← 重要! │
│     ├── 实例化所有非懒加载单例Bean               │
│     ├── 注入依赖                               │
│     └── 调用生命周期回调                        │
│ 7.12 finishRefresh()                            │
└───────────────────────┬─────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────┐
│          8. 启动收尾阶段                        │
├─────────────────────────────────────────────────┤
│ 后处理 → 发布启动事件 → 执行Runner → 发布就绪事件│
└─────────────────────────────────────────────────┘

# JVM启动和类加载

// 阶段1:JVM层面的准备工作
public class JVMStartupPhase {
    public void execute() {
        // 1. 加载JVM核心库
        loadJVMCoreLibraries();          // rt.jar, charsets.jar等
        
        // 2. 初始化类加载器层次结构
        initializeClassLoaders();        // Bootstrap → Extension → Application
        
        // 3. 加载SpringApplication主类
        Class<?> mainClass = Class.forName("com.example.MyApplication");
        
        // 4. 验证字节码安全性
        verifyBytecode(mainClass);       // 检查魔数、版本、指令合法性
        
        // 5. 准备静态变量和常量池
        prepareStaticFields();          // 初始化静态常量
        
        // 6. 调用main方法入口
        Method mainMethod = mainClass.getMethod("main", String[].class);
        mainMethod.invoke(null, (Object) args);
    }
}

// SpringBootApplication.java
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        // 阶段1: JVM启动
        SpringApplication.run(MyApplication.class, args);
    }
}

1. 加载SpringApplication类到方法区
2. 加载@SpringBootApplication注解元数据
3. 验证字节码,准备静态变量
4. 初始化Spring核心类(BeanFactory、ApplicationContext等)

# 初始化SpringApplication对象

SpringBoot整个启动流程分为两个步骤:初始化一个SpringApplication对象、执行该对象的run方法。看下SpringApplication的初始化流程,SpringApplication的构造方法:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

初始化流程中最重要的就是通过 SpringFactoriesLoader 找到 spring.factories 文件中配置的 ApplicationContextInitializer 和 ApplicationListener 两个接口的实现类名称,以便后期构造相应的实例。 ApplicationContextInitializer 的主要目的是在 ConfigurableApplicationContext 做 refresh之前,对ConfigurableApplicationContext实例做进一步的设置或处理。ConfigurableApplicationContext继承自 ApplicationContext ,其主要提供了对 ApplicationContext 进行设置的能力。

Spring Boot提供两种方式来添加自定义监听器

通过 SpringApplication.addListeners()或者 SpringApplication.setListeners()两个方法来添加一个或者多个自定义监听器

既然SpringApplication的初始化流程中已经从 spring.factories中获取到 ApplicationListener的实现类,那么我们直接在自己的jar包的 META-INF/spring.factories文件中新增配置即可:

# 执行该对象的run方法

Spring Boot应用的整个启动流程都封装在 SpringApplication.run 方法中,其整个流程真的是太长太长了,但本质上就是在Spring容器启动的基础上做了大量的扩展,按照这个思路来看看源码

Spring Boot启动调用栈(层次化视图):

┌─────────────────────────────────────────────────┐
│           SpringApplication.run()               │
├─────────────────────────────────────────────────┤
│ 1. 启动准备                                      │
│    ├── configureHeadlessProperty()              │
│    ├── getRunListeners()                        │
│    └── listeners.starting() ← 发布启动事件       │
│                                                │
│ 2. 环境准备                                      │
│    ├── new DefaultApplicationArguments(args)    │
│    ├── prepareEnvironment() ← 核心!            │
│    │   └── 加载所有配置源                        │
│    │       ├── application.properties          │
│    │       ├── application.yml                 │
│    │       ├── 环境变量                         │
│    │       └── 命令行参数                       │
│    ├── configureIgnoreBeanInfo()               │
│    └── printBanner()                           │
│                                                │
│ 3. 应用上下文创建                                │
│    └── createApplicationContext()               │
│        ├── Web应用: AnnotationConfigServletWebServerApplicationContext│
│        └── 普通应用: AnnotationConfigApplicationContext│
│                                                │
│ 4. 异常报告器初始化                              │
│    └── getSpringFactoriesInstances()            │
│                                                │
│ 5. 上下文预处理                                  │
│    └── prepareContext()                         │
│        ├── 设置环境environment                 │
│        ├── 执行ApplicationContextInitializer    │
│        ├── 发布ApplicationPreparedEvent        │
│        └── 加载Bean定义源                      │
│                                                │
│ 6. 容器刷新 ← 真正核心!                        │
│    └── refreshContext() → context.refresh()     │
│        ├── 6.1 prepareRefresh()                │
│        ├── 6.2 obtainFreshBeanFactory()        │
│        ├── 6.3 prepareBeanFactory()            │
│        ├── 6.4 postProcessBeanFactory()        │
│        ├── 6.5 invokeBeanFactoryPostProcessors() ← 重要!│
│        │   ├── 处理@Configuration类            │
│        │   ├── 扫描@Component注解类            │
│        │   ├── 解析@PropertySource            │
│        │   └── 加载@AutoConfiguration         │
│        ├── 6.6 registerBeanPostProcessors()     │
│        ├── 6.7 initMessageSource()             │
│        ├── 6.8 initApplicationEventMulticaster()│
│        ├── 6.9 onRefresh() ← 重要!            │
│        │   └── 创建并启动内嵌服务器             │
│        ├── 6.10 registerListeners()            │
│        ├── 6.11 finishBeanFactoryInitialization() ← 重要!│
│        │   └── 实例化所有非懒加载单例Bean        │
│        │       ├── 创建Bean实例               │
│        │       ├── 依赖注入                   │
│        │       └── 初始化回调                 │
│        └── 6.12 finishRefresh()                │
│                                                │
│ 7. 启动收尾                                      │
│    ├── afterRefresh()                          │
│    ├── listeners.started() ← 发布启动完成事件    │
│    ├── callRunners() ← 执行ApplicationRunner   │
│    └── listeners.running() ← 发布应用就绪事件    │
└─────────────────────────────────────────────────┘

核心代码

/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		// ============ 阶段0: 启动准备 ============
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 1. 设置无头模式(确保在服务器环境运行)
		configureHeadlessProperty();
		// 2. 获取并启动所有SpringApplicationRunListener
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting(); // 发布ApplicationStartingEvent
		try {
			// ============ 阶段1: 环境准备 ============
			// 3. 创建应用参数封装
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 4. 准备环境(⚠️ 这是真正的第一步核心操作!)
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			// ✅ 这里加载所有配置:application.properties → application.yml → 环境变量 → 命令行参数
        
			// 5. 配置忽略的Bean信息(AOP相关)
			configureIgnoreBeanInfo(environment);
			// 6. 打印Banner(应用启动logo)
			Banner printedBanner = printBanner(environment);
			
			// ============ 阶段2: 上下文创建 ============
			// 7. 创建应用上下文(根据应用类型)
			context = createApplicationContext(); 
			// Web应用: AnnotationConfigServletWebServerApplicationContext
			// 普通应用: AnnotationConfigApplicationContext
			
			// 8. 初始化异常报告器
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			
			// ============ 阶段3: 上下文预处理 ============
			// 9. 准备上下文(⚠️ 关键预处理)
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			// ✅ 这里包含:设置环境、执行ApplicationContextInitializer、加载Bean定义等
			
			// ============ 阶段4: 容器刷新 ============
			// 10. 刷新上下文(⚠️ 最核心的阶段!)
			refreshContext(context);
			// ✅ 这里执行完整的ApplicationContext.refresh(),包含:
			//    - BeanFactory准备
			//    - 执行BeanFactoryPostProcessor(包括组件扫描)
			//    - 注册BeanPostProcessor
			//    - 初始化MessageSource
			//    - 初始化事件广播器
			//    - onRefresh() → 创建内嵌Web服务器
			//    - 注册监听器
			//    - 实例化所有非懒加载单例Bean
			//    - 完成刷新
			
			// 11. 后刷新处理(空方法,可扩展)
			afterRefresh(context, applicationArguments);
			
			// ============ 阶段5: 启动完成 ============
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			
			// 12. 发布应用启动完成事件
			listeners.started(context); // 发布ApplicationStartedEvent
			
			// 13. 执行所有Runner(ApplicationRunner/CommandLineRunner)
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			// 14. 发布应用就绪事件
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

# 获取并启动所有SpringApplicationRunListener

通过 SpringFactoriesLoader 查找并加载所有的 SpringApplicationRunListeners,通过调用starting()方法通知所有的SpringApplicationRunListener:应用开始启动了。SpringApplicationRunListener 其本质上就是一个事件发布者,它在SpringBoot应用启动的不同时间点发布不同应用事件类型(ApplicationEvent),如果有哪些事件监听者(ApplicationListener)对这些事件感兴趣,则可以接收并且处理。前面的初始化流程中,SpringApplication 加载了一系列 ApplicationListener。发布事件的代码已经在SpringApplicationRunListeners中实现了。 SpringApplicationRunListener只有一个实现类: EventPublishingRunListener。此处的代码只会返回一个 SpringApplicationRunListeners ,注意后面多了一个s字母,看下源码就会发现该类就是包含了一个SpringApplicationRunListener的List。操作SpringApplicationRunListeners ,在内部会遍历每一个SpringApplicationRunListener,调用每一个SpringApplicationRunListener的实现类的starting方法

# 准备环境

创建并配置当前应用将要使用的 Environment,Environment用于描述应用程序当前的运行环境,其抽象了两个方面的内容:配置文件(profile)和属性(properties),开发经验丰富的同学对这两个东西一定不会陌生:不同的环境(eg:生产环境、预发布环境)可以使用不同的配置文件,而属性则可以从配置文件、环境变量、命令行参数等来源获取。因此,当Environment准备好后,在整个应用的任何时候,都可以从Environment中获取资源。

总结起来,主要完成以下几件事:

- 判断Environment是否存在,不存在就创建(如果是web项目就创建 StandardServletEnvironment,否则创建 StandardEnvironment)

- 配置Environment:配置profile以及properties

- 调用SpringApplicationRunListener的 environmentPrepared()方法,通知事件监听者:应用的Environment已经准备好

# 打印Banner图案

# 创建应用上下文

根据不同的ApplicationType创建不同的Context,具体的类型回顾初始化中App类型的介绍

# 准备上下文

初始化ApplicationContext,主要完成以下工作:

- 将准备好的Environment设置给ApplicationContext

- 遍历调用所有的ApplicationContextInitializer的 initialize()方法来对已经创建好的ApplicationContext进行进一步的处理

- 调用SpringApplicationRunListener的 contextPrepared()方法,通知所有的监听者:ApplicationContext已经准备完毕

- 将所有的bean加载到容器中

- 调用SpringApplicationRunListener的 contextLoaded()方法,通知所有的监听者:ApplicationContext已经装载完毕

# 容器刷新

refresh完成配置类的解析、各种BeanFactoryPostProcessor和BeanPostProcessor的注册、国际化配置的初始化、web内置容器的构造等等。

以上就是Spring Boot的整个启动流程,其核心就是在Spring容器初始化并启动的基础上加入各种扩展点,这些扩展点包括:

  • ApplicationContextInitializer
  • ApplicationListener
  • 自动配置自定义 org.springframework.boot.env.EnvironmentPostProcessor: Allows for customization of the application's {@link Environment} prior to the application context being refreshed.
  • 各种BeanFactoryPostProcessor等等 org.springframework.beans.factory.config.BeanFactoryPostProcessor: 允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做一些额外的操作,比如修改bean定义的某些属性或者增加其他信息等。

# 实例分析启动流程

// 1. 主启动类
@SpringBootApplication
public class SimpleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SimpleApplication.class, args);
    }
}

// 2. 一个简单的RestController
@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello World!";
    }
}

// 3. 配置文件application.properties
server.port=8080
spring.application.name=simple-demo

# 阶段1: JVM启动(底层准备)

java -jar simple-app.jar

加载JVM核心库

初始化类加载器层次结构

加载SimpleApplication.class到内存

验证字节码安全性

调用main方法入口

# 阶段2: SpringApplication.run() 初始化

SpringApplication.run(SimpleApplication.class, args);

// 内部创建SpringApplication实例
SpringApplication app = new SpringApplication(SimpleApplication.class);
app.run(args);

// 2.1 配置无头模式(确保在服务器环境运行)
configureHeadlessProperty(); // java.awt.headless=true

// 2.2 获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(); // 发布ApplicationStartingEvent

// 2.3 创建应用参数封装
ApplicationArguments appArgs = new DefaultApplicationArguments(args);
// 解析命令行参数:--server.port=9090 等
# 发布启动事件 ⭐扩展点1: 启动事件监听

listeners.starting(); // 发布ApplicationStartingEvent

开发者扩展方式:

// 方式1: 实现ApplicationListener
@Component
public class MyStartingListener implements ApplicationListener<ApplicationStartingEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartingEvent event) {
        System.out.println("应用开始启动...");
        // 场景:记录启动日志、初始化监控指标
    }
}

// 方式2: 使用@EventListener注解
@Component
public class EventListenerService {
    @EventListener
    public void handleStartingEvent(ApplicationStartingEvent event) {
        // 场景:验证环境配置、检查依赖服务
        checkRequiredProperties();
    }
    
    private void checkRequiredProperties() {
        // 检查必需配置是否存在
        if (System.getProperty("required.config") == null) {
            throw new IllegalStateException("缺少必需配置");
        }
    }
}

常见场景:

启动日志记录:记录应用启动时间和参数

环境验证:检查必需的环境变量和配置

资源预加载:提前加载常用资源

监控指标初始化:设置应用监控

完整事件顺序:

ApplicationStartingEvent- 最早,环境还未准备

ApplicationEnvironmentPreparedEvent- 环境已准备,上下文未创建

ApplicationContextInitializedEvent- 上下文已创建,Initializer已执行

ApplicationPreparedEvent- 上下文已准备,refresh()未调用

ApplicationStartedEvent- refresh()已完成,Runner未执行

ApplicationReadyEvent- 完全就绪,Runner已执行

# 阶段3: 环境准备(加载配置)

// 3.1 加载所有配置源(按优先级)
ConfigurableEnvironment environment = prepareEnvironment(listeners, appArgs);
// 1.  application.properties → server.port=8080
// 2.  application.yml (不存在)
// 3.  环境变量 (SPRING_APPLICATION_JSON等)  
// 4.  命令行参数 (--server.port=9090)
// 5.  默认属性

=>

// SpringApplication.java
private ConfigurableEnvironment prepareEnvironment(...) {
    // 1. 创建环境对象(根据应用类型)
    ConfigurableEnvironment environment = createEnvironment(); 
    // Web应用: StandardServletEnvironment
    // 普通应用: StandardEnvironment
    
    // 2. 配置PropertySource(配置源加载在这里发生!)
    configurePropertySources(environment, args);
    
    // 3. 配置Profile
    configureProfiles(environment, args);
    
    return environment;
}

// configurePropertySources内部的加载顺序:
public void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
    // 配置源优先级(从高到低):
    // 1. 命令行参数 (--server.port=8080)
    environment.getPropertySources().addFirst(new SimpleCommandLinePropertySource(args));
    
    // 2. ServletConfig参数 (Web应用)
    // 3. ServletContext参数 (Web应用) 
    // 4. JNDI属性
    // 5. 系统环境变量
    // 6. 系统属性
    // 7. 随机值属性
    // 8. 应用配置文件 ← 主要配置来源!
    //    - application.properties
    //    - application.yml  
    //    - application-{profile}.properties
    // 9. 默认属性
}
# 加载配置源 ⭐扩展点2: 自定义PropertySource

开发者扩展方式:


@Component
public class CustomPropertySourceConfig {
    @PostConstruct
    public void addCustomPropertySource() {
        // 添加自定义配置源
        MapPropertySource customSource = new MapPropertySource("custom", 
            Collections.singletonMap("custom.property", "value"));
        environment.getPropertySources().addFirst(customSource);
    }
}

// 或者实现EnvironmentPostProcessor
public class CustomEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment env, 
                                     SpringApplication application) {
        // 修改或添加配置源
        env.getPropertySources().addFirst(...);

        // 开发者在这里操作PropertySources
        MutablePropertySources propertySources = environment.getPropertySources();
        // 添加自定义配置源或修改现有配置
        Map<String, Object> customProperties = new HashMap<>();
        customProperties.put("custom.property", "value");
        propertySources.addFirst(new MapPropertySource("custom", customProperties));
    }
}

常见场景:

数据库配置加密:解密加密的数据库密码

外部配置集成:从外部配置中心加载配置

环境特定配置:根据运行环境动态调整配置

# printBanner
// 3.2 配置忽略的Bean信息(AOP相关)
configureIgnoreBeanInfo(environment);

// 3.3 打印Banner
// 控制台输出Spring logo和版本信息
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

=>

// configureIgnoreBeanInfo的实际作用:
protected void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
    // 设置系统属性,告诉Spring跳过某些Bean的元数据解析
    // 这可以显著提高启动性能,特别是当有大量Bean时
    
    String ignore = environment.getProperty("spring.aop.ignore", "true");
    if ("true".equals(ignore)) {
        // 设置系统属性,优化AOP处理
        System.setProperty("spring.aop.auto", "false");
        
        // 对于某些已知的基础设施Bean,跳过AOP代理创建
        // 比如:BeanFactoryPostProcessor, BeanPostProcessor等
        // 这些Bean不需要AOP代理,跳过检查可以提高性能
    }
}

开发者扩展方式:

// 自定义Banner
public class CustomBanner implements Banner {
    @Override
    public void printBanner(Environment environment, 
                           Class<?> sourceClass, PrintStream out) {
        out.println("=== 我的应用 ===");
        out.println("版本: " + environment.getProperty("app.version"));
    }
}

// 在application.properties中指定
spring.banner.location=classpath:my-banner.txt

# 阶段4: 应用上下文创建

context = createApplicationContext();
// 对于AnnotationConfigApplicationContext:
// 在构造器中就创建了DefaultListableBeanFactory
// 但此时是空的,只有基础设施

=>

// 检测到有spring-web依赖,创建Web容器
return new AnnotationConfigServletWebServerApplicationContext();

// 此时还是空容器,没有任何Bean

# 阶段5: 上下文预处理

prepareContext(context, environment, listeners, appArgs, printedBanner); =>

// 5.1 设置环境到上下文
context.setEnvironment(environment);

// 5.2 执行ApplicationContextInitializer(如果有)
applyInitializers(context);

// 发布ApplicationContextInitializedEvent
listeners.contextInitialized(context);

// 注册特定的单例Bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 这里注册的是已经实例化的对象,不是BeanDefinition
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
    beanFactory.registerSingleton("springBootBanner", printedBanner);
}

if (beanFactory instanceof DefaultListableBeanFactory) {
    ((DefaultListableBeanFactory) beanFactory)
        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}

// 设置懒初始化(Spring Boot 2.2+)
if (this.lazyInitialization) {
    context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}

// 加载源(不是register(SimpleApplication.class))
// 将SimpleApplication.class转换为BeanDefinition存入BeanFactory
// 但此时只是蓝图,还没有实例化
Set<Object> sources = getAllSources(); // 获取所有配置源,包括主类SimpleApplication
// load方法会处理配置类
load(context, sources.toArray(new Object[0]));
// BeanDefinitionLoader.load() 方法会:
// 1. 如果source是Class且有@Configuration注解,注册为配置类
// 2. 处理@ComponentScan,扫描指定包下的@Component类
// 3. 处理@Import、@ImportResource等注解
// 4. 为SimpleApplication创建BeanDefinition并注册到BeanFactory

// 简单说:load()将SimpleApplication.class转换为BeanDefinition并注册
// SimpleApplication.class → 作为配置类注册 → 解析注解 → 扫描包 → 注册其他Bean
// 所以不是注册SimpleApplication本身,而是:
// 注册SimpleApplication为配置类(BeanDefinition)
// 解析其上的注解(@ComponentScan, @EnableAutoConfiguration)
// 执行组件扫描找到其他Bean
// 处理自动配置加载spring.factories中的配置

// 5.4 发布ApplicationPreparedEvent
listeners.contextPrepared(context);
# 执行ApplicationContextInitializer ⭐扩展点3: 上下文初始化器

applyInitializers(context); =>

// 执行所有ApplicationContextInitializer
for (ApplicationContextInitializer initializer : initializers) {
    initializer.initialize(context);
}

开发者扩展方式:

// 方式1: 实现ApplicationContextInitializer
public class MyContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext context) {
        // 场景1: 早期Bean注册,实际用例:需要在其他组件的配置阶段就提供服务的基础设施Bean。
        context.getBeanFactory().registerSingleton("earlyService", new EarlyService());
        // 这个Bean现在可以在任何BeanFactoryPostProcessor中使用
        // 比如在自定义的BeanFactoryPostProcessor中:
        // ConfigService config = beanFactory.getBean("configService");

        // 场景2: 环境验证
        Environment env = context.getEnvironment();
        if (!env.containsProperty("db.url")) {
            throw new IllegalStateException("数据库配置缺失");
        }
        
        // 场景3: 动态配置
        if (env.acceptsProfiles("cloud")) {
            System.setProperty("server.port", "8081");
        }
    }
}

// 注册方式1: spring.factories
// META-INF/spring.factories:
org.springframework.context.ApplicationContextInitializer=com.example.MyContextInitializer

// 注册方式2: 启动类设置
new SpringApplicationBuilder(MyApp.class)
    .initializers(new MyContextInitializer())
    .run(args);

常见场景:

早期Bean注册:在容器刷新前注册单例Bean

环境验证:检查必需配置是否就绪

动态配置:根据环境动态修改配置

自定义BeanFactory设置:修改BeanFactory配置

# beanFactory.registerSingleton

注意: beanFactory.registerSingleton("springApplicationArguments", applicationArguments);

# 启动Spring Boot应用时传入命令行参数
java -jar myapp.jar --debug --server.port=9090 --spring.profiles.active=dev inputfile.txt

在Spring Boot中,这些参数被封装为:
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

选项参数(Option Arguments):以--开头

--debug→ 选项名:"debug",值:空列表

--server.port=9090→ 选项名:"server.port",值:["9090"]

--spring.profiles.active=dev→ 选项名:"spring.profiles.active",值:["dev"]

非选项参数(Non-option Arguments):不以--开头

inputfile.txt→ 非选项参数


// 方式1:注入使用
@Component
public class MyComponent {
    @Autowired
    private ApplicationArguments args;
    
    public void checkArgs() {
        // 检查是否有debug选项
        if (args.containsOption("debug")) {
            System.out.println("Debug模式已启用");
        }
        
        // 获取server.port的值
        List<String> portValues = args.getOptionValues("server.port");
        if (!portValues.isEmpty()) {
            String port = portValues.get(0);
            System.out.println("端口号: " + port);
        }
        
        // 获取非选项参数
        List<String> nonOptionArgs = args.getNonOptionArgs();
        System.out.println("非选项参数: " + nonOptionArgs);
    }
}

// 方式2:在BeanFactoryPostProcessor中使用
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        ApplicationArguments args = beanFactory.getBean("springApplicationArguments", ApplicationArguments.class);
        
        if (args.containsOption("dev")) {
            // 开发环境特殊配置
            enableDevelopmentFeatures();
        }
    }
}
# 为什么需要类型判断 beanFactory instanceof DefaultListableBeanFactory
// 在prepareContext方法中:
if (beanFactory instanceof DefaultListableBeanFactory) {
    // 只有DefaultListableBeanFactory才有这个方法
    ((DefaultListableBeanFactory) beanFactory)
        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        // true:允许同名Bean定义覆盖(后注册的覆盖先注册的)
        // false:不允许覆盖,遇到同名Bean定义时抛出异常
}

核心原因:Spring的设计支持多种BeanFactory实现, 虽然Spring Boot默认使用DefaultListableBeanFactory,但框架设计上支持多种实现:

// Spring支持的不同BeanFactory实现:
public interface ConfigurableListableBeanFactory 
    extends ListableBeanFactory, ConfigurableBeanFactory, AutowireCapableBeanFactory {
    // 基础接口
}

// 主要实现类:
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
    implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    // Spring默认使用的实现
}

// 其他可能的实现(理论上):
public class CustomBeanFactory implements ConfigurableListableBeanFactory {
    // 用户自定义的BeanFactory实现
}

实际应用场景:

// 场景1:Spring Boot测试中可能使用不同的BeanFactory
@SpringBootTest
@TestPropertySource(properties = "spring.main.allow-bean-definition-overriding=true")
public class MyTest {
    // 测试环境可能使用特殊的BeanFactory配置
}

// 场景2:自定义BeanFactory(虽然少见,但框架设计上支持)
@Configuration
public class CustomBeanFactoryConfig {
    @Bean
    public ConfigurableListableBeanFactory customBeanFactory() {
        return new CustomBeanFactoryImplementation(); // 用户自定义实现
    }
}
# 发布准备事件 ⭐扩展点4: 准备事件监听

listeners.contextPrepared(context); // ApplicationPreparedEvent 开发者扩展方式:

@Component
public class PreparedEventListener implements ApplicationListener<ApplicationPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationPreparedEvent event) {
        ConfigurableApplicationContext context = event.getApplicationContext();
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        
        // 场景1: Bean定义分析(在实例化前)
        String[] beanNames = beanFactory.getBeanDefinitionNames();
        log.info("已注册Bean定义数量: {}", beanNames.length);
        
        // 场景2: 条件配置验证
        if (!beanFactory.containsBeanDefinition("requiredBean")) {
            throw new IllegalStateException("必需Bean未配置");
        }
        
        // 场景3: 动态注册配置(基于现有Bean定义)
        if (beanFactory.containsBeanDefinition("dataSource")) {
            // 根据现有配置决定是否注册额外组件
            registerAdditionalComponents(beanFactory);
        }
        
        // 场景4: 性能监控设置
        setupPerformanceMonitoring(beanFactory);
        
        // 预加载缓存
        preloadCacheData();
    }
}

# 阶段6: 容器刷新(核心!)

refreshContext(context); // → 调用context.refresh() =>

AbstractApplicationContext:

public void refresh() {
    // 7.1 prepareRefresh() - 设置上下文激活状态、初始化属性源等
    prepareRefresh();
    
    // 7.2 obtainFreshBeanFactory() - 获取或刷新BeanFactory
    // 对于AnnotationConfigApplicationContext,这里会创建DefaultListableBeanFactory
    // 但此时是空的,还没有Bean定义
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
    // 7.3 prepareBeanFactory() - 配置BeanFactory的基础设施
    // 设置类加载器、表达式解析器、属性编辑器
    // 注册环境Bean等基础配置:environment、systemProperties、systemEnvironment
    prepareBeanFactory(beanFactory);
    // 此时BeanFactory中已有:
    // - 几个registerSingleton的单例Bean
    // - 一些BeanDefinition(来自load())
    // 但基础设施(如环境Bean、解析器等)还需要配置
    
    
    // 7.4 postProcessBeanFactory() - 空方法,子类可重写
    postProcessBeanFactory(beanFactory);
    // Web应用在这里注册Servlet相关的Scope
}
# 6.1-6.4: 准备阶段
prepareRefresh();
obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
postProcessBeanFactory(beanFactory);

=>

// BeanFactory基础设施设置过程:
prepareBeanFactory(beanFactory)
│
├── 设置类加载器
│   └── beanFactory.setBeanClassLoader()
│
├── 设置表达式解析器
│   └── beanFactory.setBeanExpressionResolver()
│       └── StandardBeanExpressionResolver ← 支持SPEL表达式
│
├── 添加属性编辑器注册器
│   └── beanFactory.addPropertyEditorRegistrar()
│       └── ResourceEditorRegistrar ← 注册资源编辑器
│
├── 添加ApplicationContextAware处理器
│   └── beanFactory.addBeanPostProcessor()
│       └── ApplicationContextAwareProcessor ← 处理Aware接口
│
├── 设置忽略的依赖接口
│   └── beanFactory.ignoreDependencyInterface()
│       └── 避免某些接口的自动注入
│
├── 注册可解析的依赖
│   └── beanFactory.registerResolvableDependency()
│       └── 注册BeanFactory、ResourceLoader等
│
└── 添加其他后处理器
    └── beanFactory.addBeanPostProcessor()
        └── ApplicationListenerDetector ← 检测应用监听器

Spring基础设施组件对比总结表

组件 在BeanFactory中的角色 主要功能 典型使用场景 关联注解/表达式 配置时机
EmbeddedValueResolver(嵌入式值解析器) 解析字符串中的占位符和SpEL表达式 将包含${...}和#{...}的字符串解析为实际值 @Value注解处理配置文件占位符解析SpEL表达式求值 @Value("${server.port}")@Value("#{T(java.lang.Math).random() * 100.0}")@Value("#{systemProperties['user.home']}") prepareBeanFactory()阶段
PropertyEditor(属性编辑器) 传统类型转换机制 将字符串转换为目标类型(如String→Date) XML配置中的类型转换@Value注解的简单类型转换表单数据绑定 @Value("2023-01-01") → Date对象XML配置中的属性转换 prepareBeanFactory()阶段注册
ExpressionParser(表达式解析器) SpEL表达式求值引擎 解析和执行Spring表达式语言 @Value中的复杂表达式安全导航操作符条件表达式 @Value("#{user?.address?.city}")@Value("#{environment.acceptsProfiles('dev') ? 'dev' : 'prod'}")@Value("#{users.}") prepareBeanFactory()阶段配置
ScopeManager(作用域管理器) 管理Bean的生命周期范围 控制Bean的创建、缓存和销毁策略 单例vs原型作用域Request/Session作用域自定义作用域 @Scope("prototype")@Scope("request")@Scope("session") BeanFactory初始化阶段
ConversionService(类型转换服务) 现代统一类型转换API 提供类型安全的转换机制,支持泛型和集合 配置属性转换表单数据绑定自定义类型转换 @Value("${some.list}") List@Value("${some.map}") Map自定义类型转换 BeanFactory后处理阶段
Conditional系统(条件化配置) 基于条件的Bean创建 根据条件决定是否创建Bean 环境特定配置特性开关控制类路径检测 @Profile("cloud")@ConditionalOnProperty("feature.enabled")@ConditionalOnClass("com.example.Service") Bean定义加载阶段
# 6.5: 执行BeanFactoryPostProcessor ⭐扩展点5: Bean工厂后处理

invokeBeanFactoryPostProcessors(); =>

// 6.5.1 处理@SpringBootApplication
// → 包含@ComponentScan,扫描com.example包

// 6.5.2 找到HelloController(有@RestController注解)
// @RestController包含@Component,所以被扫描到

// 6.5.3 创建BeanDefinition(Bean蓝图)
BeanDefinition controllerDef = new RootBeanDefinition(HelloController.class);
controllerDef.setScope(SCOPE_SINGLETON);

// 6.5.4 注册到BeanFactory
beanFactory.registerBeanDefinition("helloController", controllerDef);

// 6.5.5 处理自动配置(@EnableAutoConfiguration)
// 自动配置内嵌服务器、JSON转换器等

为什么需要 BeanFactoryPostProcessor(后处理)

核心答案:动态性和条件化。有些配置无法在编写代码时确定,需要在运行时根据环境决定.

处理对象:BeanDefinition(Bean蓝图)

开发者扩展方式:

// 方式1: 实现BeanFactoryPostProcessor
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        Environment env = beanFactory.getBean(Environment.class);
        
        // 场景1: 根据环境动态修改配置
        if (env.acceptsProfiles("cloud")) {
            BeanDefinition dbDef = beanFactory.getBeanDefinition("dataSource");
            // 云环境使用连接池配置
            dbDef.getPropertyValues().add("url", env.getProperty("CLOUD_DB_URL"));
        }
        
        // 场景2: 条件注册Bean
        if (isFeatureEnabled(env)) {
            GenericBeanDefinition featureBean = new GenericBeanDefinition();
            featureBean.setBeanClass(FeatureService.class);
            ((DefaultListableBeanFactory) beanFactory)
                .registerBeanDefinition("featureService", featureBean);
        }
        
        // 场景3: 属性解密
        BeanDefinition[] beanDefs = beanFactory.getBeanDefinitions();
        for (BeanDefinition beanDef : beanDefs) {
            decryptPropertyValues(beanDef); // 解密加密的属性值
        }
    }
}

常见场景:

Bean定义修改:改变作用域、懒加载设置等

条件注册:根据条件动态注册Bean

AOP代理设置:为特定Bean设置代理

属性加密解密:处理加密的配置属性

# 6.6: 注册BeanPostProcessor ⭐扩展点6: Bean后处理
// 注册AutowiredAnnotationBeanPostProcessor
// 用于后续处理@Autowired注入
registerBeanPostProcessors(beanFactory);

处理对象:Bean实例(实际对象)

开发者扩展方式:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    
    // Bean初始化前调用
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        // 场景1: 监控统计
        if (bean instanceof RestController) {
            monitorBeanCreation(beanName, bean.getClass());
        }
        
        // 场景2: 自定义注解处理
        if (bean.getClass().isAnnotationPresent(MyAnnotation.class)) {
            processCustomAnnotation(bean);
        }
        
        return bean;
    }
    
    // Bean初始化后调用
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        // 场景1: AOP代理创建 这个不是正常的场景而是springboot源码spring-boot-starter-aop的实现,纯粹是举例
        if (bean instanceof MyService) {
            return createProxy(bean); // 返回代理对象
        }
        
        // 场景2: 缓存包装
        if (bean instanceof Repository) {
            return new CachingRepositoryWrapper((Repository) bean);
        }
        
        return bean;
    }
}

常见场景:

AOP代理:为Bean创建动态代理

监控统计:记录Bean创建和调用统计

缓存包装:为数据访问层添加缓存

自定义注解处理:处理业务自定义注解

# 6.7-6.8: 消息源和事件广播器
initMessageSource();
initApplicationEventMulticaster();
# 6.9: 子类特定刷新 ⭐扩展点7: 内嵌服务器自定义

onRefresh(); // ServletWebServerApplicationContext.onRefresh() =>

// 6.9.1 创建内嵌Tomcat服务器
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080); // 从配置读取

// 6.9.2 创建DispatcherServlet(Spring MVC核心)
DispatcherServlet dispatcherServlet = new DispatcherServlet(context);

// 6.9.3 注册Servlet映射
context.addServlet("dispatcher", dispatcherServlet).addMapping("/");

// 6.9.4 启动Tomcat(但还没开始监听端口)
tomcat.start();

开发者扩展方式:

// 自定义内嵌服务器配置
@Configuration
public class ServerConfig {
    
    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.setPort(8080);
        tomcat.addConnectorCustomizers(connector -> {
            // 自定义连接器配置
            connector.setProperty("maxThreads", "200");
        });
        return tomcat;
    }
    
    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }
}
# 6.10: 注册监听器

registerListeners();

# 6.11: 完成BeanFactory初始化 ⭐扩展点8: Bean生命周期回调

finishBeanFactoryInitialization(); =>

// 6.11.1 实例化HelloController
HelloController controller = new HelloController(); // 调用构造方法

// 6.11.2 依赖注入(本例没有依赖,跳过)

// 6.11.3 初始化回调
// 调用@PostConstruct方法(本例没有)

// 6.11.4 注册到单例池
singletonObjects.put("helloController", controller);

开发者扩展方式:

@Component
public class MyService implements InitializingBean, DisposableBean {
    
    // InitializingBean - 初始化回调
    @Override
    public void afterPropertiesSet() throws Exception {
        // 场景:资源初始化
        initializeResources();
        loadReferenceData();
    }
    
    // DisposableBean - 销毁回调  
    @Override
    public void destroy() throws Exception {
        // 场景:资源清理
        cleanupResources();
        closeConnections();
    }
    
    // 或使用注解方式
    @PostConstruct
    public void init() {
        // 初始化逻辑
    }
    
    @PreDestroy
    public void cleanup() {
        // 清理逻辑
    }
}
# 6.12: 完成刷新 ⭐扩展点9: 上下文刷新事件
// 发布ContextRefreshedEvent事件
// 完成容器初始化
finishRefresh();

开发者扩展方式:

@Component
public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 场景:容器刷新完成后执行初始化
        ApplicationContext context = event.getApplicationContext();
        
        // 验证所有必需Bean是否就绪
        validateRequiredBeans(context);
        
        // 启动后台任务
        startBackgroundJobs();
    }
}

# 阶段7: 启动收尾

// 7.1 后处理(空方法,可扩展)
afterRefresh(context, applicationArguments);

// 7.2 发布启动完成事件 ⭐扩展点10: 启动完成事件
listeners.started(context); // ApplicationStartedEvent

// 7.3 执行Runner(如果有ApplicationRunner/CommandLineRunner)⭐扩展点11: 启动Runner
callRunners(context, appArgs);

// 7.4 发布应用就绪事件 ⭐扩展点12: 应用就绪事件 
listeners.running(context); // ApplicationReadyEvent

// 7.5 Tomcat开始监听端口(真正接受请求)
// 控制台输出: Tomcat started on port(s): 8080 (http)

Runner扩展方式:

@Component
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 场景1: 数据初始化
        if (args.containsOption("init-data")) {
            initializeSampleData();
        }
        
        // 场景2: 连接检查
        checkExternalServices();
        
        // 场景3: 缓存预热
        warmUpCaches();
    }
}

@Component  
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        // 处理命令行参数
        for (String arg : args) {
            if (arg.equals("--debug")) {
                enableDebugMode();
            }
        }
    }
}

# 时序状态和最终状态

时间点 T0: 创建BeanFactory(空)

// 刚创建时的BeanFactory内容:
BeanFactory状态:
├── singletonObjects: {}                    // 空单例池
├── beanDefinitionMap: {}                  // 空Bean定义映射
├── embeddedValueResolvers: []              // 空值解析器
└── 基本基础设施: 
    ├── 类加载器
    ├── 类型转换服务
    └── 基本属性编辑器

时间点 T1: registerSingleton("springApplicationArguments") → 添加具体对象

// 添加具体对象后的状态:
BeanFactory状态:
├── singletonObjects: 
│   └── "springApplicationArguments" → ApplicationArguments实例
├── beanDefinitionMap: {}                  // 仍然无Bean定义
├── embeddedValueResolvers: []
└── 基本基础设施

时间点 T2: load() → 注册BeanDefinition(蓝图)

// load()处理SimpleApplication.class后的状态:
BeanFactory状态:
├── singletonObjects: 
│   └── "springApplicationArguments" → ApplicationArguments实例
├── beanDefinitionMap: 
│   ├── "simpleApplication" → BeanDefinition(SimpleApplication.class)
│   ├── "helloController" → BeanDefinition(HelloController.class)   // 扫描到的
│   ├── "userService" → BeanDefinition(UserService.class)           // 扫描到的
│   └── 其他自动配置的BeanDefinition...
├── embeddedValueResolvers: []
└── 基本基础设施

时间点 T3: prepareBeanFactory() → 配置BeanFactory基础设施

// 配置基础设施后的状态:
BeanFactory状态:
├── singletonObjects: 
│   ├── "springApplicationArguments" → ApplicationArguments实例
│   └── "environment" → Environment实例              // 新添加的环境Bean
├── beanDefinitionMap: 
│   ├── "simpleApplication" → BeanDefinition
│   ├── "helloController" → BeanDefinition
│   └── ...
├── embeddedValueResolvers: [默认值解析器]           // 新增
└── 增强的基础设施:
    ├── 完整的属性编辑器
    ├── 表达式解析器
    ├── 作用域管理器
    └── 类型转换服务

时间点 T4: invokeBeanFactoryPostProcessors() → 处理所有BeanDefinition

// 处理后处理器后的状态(可能的变化):
BeanFactory状态:
├── singletonObjects: 
│   ├── "springApplicationArguments" → ApplicationArguments实例
│   └── "environment" → Environment实例
├── beanDefinitionMap: 
│   ├── "simpleApplication" → BeanDefinition(可能被修改)
│   ├── "helloController" → BeanDefinition(可能被修改)
│   ├── "userService" → BeanDefinition(可能被修改)
│   ├── "newDynamicBean" → BeanDefinition(可能新增) // 动态注册的
│   └── ...
├── embeddedValueResolvers: [默认值解析器]
└── 基础设施

时间点 T5: finishBeanFactoryInitialization() → 实例化所有单例Bean

// 实例化所有单例Bean后的状态:
BeanFactory状态:
├── singletonObjects: 
│   ├── "springApplicationArguments" → ApplicationArguments实例
│   ├── "environment" → Environment实例
│   ├── "simpleApplication" → SimpleApplication实例(已实例化)
│   ├── "helloController" → HelloController实例(已实例化+依赖注入)
│   ├── "userService" → UserService实例(已实例化+依赖注入)
│   └── 所有其他单例Bean...
├── beanDefinitionMap: 
│   ├── "simpleApplication" → BeanDefinition
│   ├── "helloController" → BeanDefinition
│   └── ...(定义仍然保留)
├── embeddedValueResolvers: [默认值解析器]
└── 基础设施

应用启动完成后的状态:

├── BeanFactory中有:
│   └── helloController : HelloController实例
├── Web服务器:
│   └── Tomcat运行在8080端口
├── URL映射:
│   └── /hello → HelloController.hello()方法
└── 可以处理请求:
    GET http://localhost:8080/hello → "Hello World!"

# Bean的时序状态细节

# 创建-注入-回调

更深一步观察bean的整个生命周期:

// Spring容器启动入口
SpringApplication.run()
└── AbstractApplicationContext.refresh()
    └── finishBeanFactoryInitialization(beanFactory)
        └── DefaultListableBeanFactory.preInstantiateSingletons()
            └── 遍历所有Bean定义,对每个Bean执行:
                getBean(beanName) → doGetBean() → createBean() → doCreateBean()
                    ├── createBeanInstance()    // 阶段1: 实例化(构造器注入)
                    ├── populateBean()          // 阶段2: 依赖注入  (字段/setter注入)  
                    └── initializeBean()        // 阶段3: 初始化生命周期回调)
					
// 单个Bean的详细创建过程:
doCreateBean(beanName, mbd, args)
│
├── 1. createBeanInstance()     ← 实例化阶段(构造器注入在这里完成)
│   │
│   ├── 1.1 确定实例化策略
│   │   ├── determineConstructorsFromBeanPostProcessors()  // 通过后处理器选择构造器
│   │   └── 根据配置选择三种路径之一:
│   │
│   ├── 1.2 构造器注入路径 (autowireConstructor) ← 主要注入方式
│   │   ├── determineConstructor()              // 确定使用哪个构造器
│   │   ├── resolveConstructorArguments()      // 解析构造器参数(核心依赖解析!)
│   │   │   └── resolveDependency()             // 解析单个依赖
│   │   │       └── getBean()                  // 可能递归获取依赖Bean
│   │   └── constructor.newInstance(args)      // 使用参数创建实例
│   │
│   ├── 1.3 默认构造器路径 (instantiateBean)
│   │   ├── getInstantiationStrategy()          // 获取实例化策略
│   │   └── instantiate()                       // 反射创建空实例
│   │
│   └── 1.4 工厂方法路径 (instantiateUsingFactoryMethod)
│       ├── determineFactoryMethod()            // 确定工厂方法
│       ├── resolveArguments()                  // 解析方法参数
│       └── method.invoke()                     // 调用工厂方法
│
├── 2. populateBean()          ← 依赖注入阶段(字段/setter注入在这里完成)
│   │
│   ├── 2.1 自动注入基础支持
│   │   ├── autowireByName()                    // 按名称自动注入
│   │   └── autowireByType()                    // 按类型自动注入
│   │
│   ├── 2.2 注解驱动注入(核心!)
│   │   └── postProcessProperties()            // 调用InstantiationAwareBeanPostProcessor
│   │       └── AutowiredAnnotationBeanPostProcessor工作流程:
│   │           ├── findAutowiringMetadata()    // 查找@Autowired、@Value、@Inject注解
│   │           ├── resolveDependency()          // 解析依赖
│   │           ├── Field.set(bean, value)      // 字段反射注入
│   │           └── Method.invoke(bean, args)   // setter方法调用注入
│   │
│   └── 2.3 应用属性值
│       └── applyPropertyValues()              // 最终设置属性值
│
└── 3. initializeBean()         ← 初始化阶段(方法注入在这里完成 这里说的"方法注入"不是依赖注入,而是指初始化方法的调用)
    │
    ├── 3.1 Aware接口回调
    │   └── invokeAwareMethods()
    │       ├── setBeanName()                   // BeanNameAware
    │       ├── setBeanFactory()                // BeanFactoryAware
    │       └── setApplicationContext()         // ApplicationContextAware
    │
    ├── 3.2 前置初始化处理
    │   └── applyBeanPostProcessorsBeforeInitialization()
    │       └── CommonAnnotationBeanPostProcessor处理:
    │           └── postProcessBeforeInitialization() // 调用@PostConstruct方法
    │
    ├── 3.3 初始化方法调用
    │   └── invokeInitMethods()
    │       ├── afterPropertiesSet()            // InitializingBean接口
    │       └── initMethod.invoke()             // 自定义init方法
    │
    └── 3.4 后置初始化处理
        └── applyBeanPostProcessorsAfterInitialization()
            └── AbstractAutoProxyCreator处理:
                └── postProcessAfterInitialization() // AOP代理在这里创建
# 增强实例
// 1. 主启动类(不变)
@SpringBootApplication
public class SimpleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SimpleApplication.class, args);
    }
}

// 2. 增强的RestController,添加各种初始化方法
@RestController
public class HelloController implements InitializingBean, ApplicationContextAware {
    
    private ApplicationContext applicationContext;
    private String startupTime;
    
    // 构造器注入(阶段1)
    public HelloController() {
        System.out.println("1. 构造器调用 - HelloController实例创建");
        // environment.getProperty("spring.application.name"); // ❌ 这里不能调用 environment,可以用下面方法:
        /**
        public HelloController(Environment environment) {
            this.environment = environment;
            this.appName = environment.getProperty("spring.application.name");
            
            System.out.println("构造器中使用配置: " + appName); // ✅ 安全
        }**/

        // ❌ 输出: applicationContext = null
        // ❌ 不能在这里使用applicationContext!
        // 跟environment不同,不建议构造器注入ApplicationContext    public HelloController(ApplicationContext applicationContext) ⚠️ 技术上可用,但有风险!	有循环依赖风险,比如:
        //❌ 危险操作1: 获取其他Bean可能触发循环依赖        SomeService service = context.getBean(SomeService.class);
        // ❌ 危险操作2: 如果SomeService也依赖HelloController,会导致栈溢出    service.initialize();
        // ❌ 危险操作3: Bean可能还未完全初始化 SomeBean bean = context.getBean(SomeBean.class);
    }
    

    // 字段注入(阶段2)
    @Autowired
    private Environment environment;
    
    // Setter注入(阶段2)
    private MessageService messageService;
    
    @Autowired
    public void setMessageService(MessageService messageService) {
        System.out.println("2. Setter注入 - MessageService注入");
        this.messageService = messageService;

        // ❌ 不安全操作:
        // - 调用messageService的方法(可能其他依赖还未注入)
        // - 访问其他@Autowired字段(可能还未注入)
    }
    
    // ApplicationContextAware接口(阶段3-1)
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("3. Aware接口 - ApplicationContext设置");
        this.applicationContext = applicationContext;
    }
    
    // @PostConstruct方法(阶段3-2)
    @PostConstruct
    public void init() {
        System.out.println("4. @PostConstruct - 自定义初始化");
        this.startupTime = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        
        // 此时所有依赖都已注入完成
        String appName = environment.getProperty("spring.application.name"); // 可以安全调用 environment
        System.out.println("应用名称: " + appName);

        // ✅ 这里可以安全使用 context
        // 1. 获取Bean
        MyService service = context.getBean(MessageService.class);

        // 2. 发布应用事件
        context.publishEvent(new CustomEvent(this, "事件数据"));

        // 3. 获取环境信息(Environment是ApplicationContext的一部分)
        Environment env = context.getEnvironment();
        String configValue = env.getProperty("some.config");

        // 4. 国际化消息
        String message = context.getMessage("hello.message", null, Locale.CHINA);

        // 5. 资源访问
        Resource resource = context.getResource("classpath:config/file.txt");
    }
    
    // InitializingBean接口(阶段3-3)
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("5. InitializingBean - 属性设置后初始化");
        // 可以执行额外的初始化逻辑
    }
    
    // 业务方法
    @GetMapping("/hello")
    public String hello() {
        return messageService.getMessage() + " at " + startupTime;
    }
}

// 3. 简单的Service类,用于演示依赖
@Service
public class MessageService {
    
    public MessageService() {
        System.out.println("1. MessageService构造器调用");
    }
    
    @PostConstruct
    public void init() {
        System.out.println("4. MessageService @PostConstruct");
    }
    
    public String getMessage() {
        return "Hello World from Spring Boot!";
    }
}

// 4. 配置文件application.properties
server.port=8080
spring.application.name=simple-demo
# Bean时序状态
// HelloController的完整创建过程:
doCreateBean("helloController")
│
├── 阶段1: createBeanInstance()     ← 实例化
│   └── 调用HelloController构造器
│       ✅ 状态: Bean实例已创建,但所有字段为null
│       📝 控制台输出: "1. 构造器调用 - HelloController实例创建"
│
├── 阶段2: populateBean()          ← 依赖注入
│   ├── 注入@Autowired字段: Environment environment
│   ├── 调用setter方法: setMessageService(messageService实例)
│       ✅ 状态: 依赖已注入,但未初始化
│       📝 控制台输出: "2. Setter注入 - MessageService注入"
│   └── MessageService的创建过程(递归):
│       doCreateBean("messageService")
│       ├── createBeanInstance() → "1. MessageService构造器调用"
│       ├── populateBean()        // 无依赖需要注入
│       └── initializeBean()     → "4. MessageService @PostConstruct"
│
└── 阶段3: initializeBean()         ← 初始化
    ├── 3.1 invokeAwareMethods()
    │   └── 调用setApplicationContext()
    │       ✅ 状态: ApplicationContext已设置
    │       📝 控制台输出: "3. Aware接口 - ApplicationContext设置"
    │
    ├── 3.2 applyBeanPostProcessorsBeforeInitialization()
    │   └── CommonAnnotationBeanPostProcessor处理@PostConstruct
    │       └── 调用init()方法
    │           ✅ 状态: 自定义初始化完成,Bean已就绪
    │           📝 控制台输出: "4. @PostConstruct - 自定义初始化"
    │
    ├── 3.3 invokeInitMethods()
    │   ├── 调用afterPropertiesSet() (InitializingBean接口)
    │       📝 控制台输出: "5. InitializingBean - 属性设置后初始化"
    │   └── 如果有自定义init-method,也会在这里调用
    │
    └── 3.4 applyBeanPostProcessorsAfterInitialization()
        └── 可能创建AOP代理(本例不需要)
            ✅ 最终状态: Bean完全初始化完成,可正常使用

# 扩展点场景

# 具体业务场景1:加密配置解密服务

// 在ApplicationContextInitializer中早期注册解密服务
public class DecryptionInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext context) {
        // 早期注册解密服务
        context.getBeanFactory().registerSingleton("configDecryptor", new ConfigDecryptor());
        
        // 现在其他BeanFactoryPostProcessor可以使用这个解密服务
        // 例如:解密数据库密码、API密钥等敏感配置
    }
}

// 在BeanFactoryPostProcessor中使用早期注册的Bean
@Component
public class DecryptionPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 获取早期注册的解密服务
        ConfigDecryptor decryptor = beanFactory.getBean("configDecryptor", ConfigDecryptor.class);
        
        // 解密所有加密属性
        BeanDefinition definition = beanFactory.getBeanDefinition("dataSource");
        String encryptedPassword = (String) definition.getPropertyValues().get("password");
        String decryptedPassword = decryptor.decrypt(encryptedPassword);
        definition.getPropertyValues().add("password", decryptedPassword);
    }
}

# 具体业务场景2:环境检测服务

// 早期注册环境检测服务
public class EnvironmentDetectorInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext context) {
        EnvironmentDetector detector = new EnvironmentDetector();
        context.getBeanFactory().registerSingleton("envDetector", detector);
        
        // 根据环境动态设置配置
        if (detector.isCloudEnvironment()) {
            System.setProperty("server.port", "8081");
            System.setProperty("spring.profiles.active", "cloud");
        }
    }
}

# 具体业务场景3:功能开关服务

// 早期注册功能开关服务,用于控制后续Bean的注册
public class FeatureToggleInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext context) {
        FeatureToggle toggle = new FeatureToggle();
        context.getBeanFactory().registerSingleton("featureToggle", toggle);
        
        // 其他BeanFactoryPostProcessor可以查询功能开关状态
        // 决定是否注册某些Bean
    }
}

# 自动配置原理(深入)

  • @EnableAutoConfiguration 与 SpringFactoriesLoader
  • 条件注解体系
  • 自动配置的触发时机
  • 排除与覆盖自动配置
  • Spring Boot 2.7+ 的变化(.imports 文件)

@SpringBootApplication开启组件扫描和自动配置, 而 SpringApplication.run则负责启动引导应用程序。 @SpringBootApplication是一个复合 Annotation,它将三个注解组合在一起:

  • @SpringBootConfiguration就是 @Configuration,它是Spring框架的注解,标明该类是一个 JavaConfig配置类; allow to register extra beans in the context or import additional configuration classes;

  • @ComponentScan启用组件扫描;enable @Component scan on the package where the application is located;

  • @EnableAutoConfiguration注解: 表示开启Spring Boot自动配置功能,Spring Boot会根据应用的依赖、自定义的bean、classpath下有没有某个类 等等因素来猜测你需要的bean,然后注册到IOC容器中; enable Spring Boot’s auto-configuration mechanism (opens new window)

Notes:

You should only ever add one @SpringBootApplication or @EnableAutoConfiguration annotation. We generally recommend that you add one or the other to your primary @Configuration class only. https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-auto-configuration

开始讲解原理,先看EnableAutoConfiguration:

@Target(value=TYPE)
 @Retention(value=RUNTIME)
 @Documented
 @Inherited
 @AutoConfigurationPackage
 @Import(value=AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

重点是@Import(EnableAutoConfigurationImportSelector.class),这里它将把 EnableAutoConfigurationImportSelector作为bean注入到容器中,

@Override
	public String[] selectImports(AnnotationMetadata metadata) {
		try {
			AnnotationAttributes attributes = getAttributes(metadata);
			List<String> configurations = getCandidateConfigurations(metadata,
					attributes);
			configurations = removeDuplicates(configurations);
			Set<String> exclusions = getExclusions(metadata, attributes);
			configurations.removeAll(exclusions);
			configurations = sort(configurations);
			recordWithConditionEvaluationReport(configurations, exclusions);
			return configurations.toArray(new String[configurations.size()]);
		}
		catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}

EnableAutoConfigurationImportSelector.selectImports()是何时执行的?其实这个方法会在容器启动过程中执行: AbstractApplicationContext.refresh(), 这个EnableAutoConfigurationImportSelector类会扫描所有的jar包,将所有符合条件的@Configuration配置类注入的容器中,何为符合条件,看看 META-INF/spring.factories的文件内容:

https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
.....
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
.....

然后举例看 DataSourceAutoConfiguration:

https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {

@ConditionalOnClass({DataSource.class,EmbeddedDatabaseType.class}):当Classpath中存在DataSource或者EmbeddedDatabaseType类时才启用这个配置,否则这个配置将被忽略。 注意上面的DataSourceProperties, @EnableConfigurationProperties(DataSourceProperties.class):将DataSource的默认配置类注入到IOC容器中,DataSourceproperties定义为:

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
	private ClassLoader classLoader;
	/**
	 * Name of the datasource. Default to "testdb" when using an embedded database.
	 */
	private String name;
	/**
	 * Whether to generate a random datasource name.
	 */
	private boolean generateUniqueName = true;
	/**
	 * Fully qualified name of the connection pool implementation to use. By default, it
	 * is auto-detected from the classpath.
	 */
	private Class<? extends DataSource> type;

	/**
	 * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
	 */
	private String driverClassName;

	/**
	 * JDBC URL of the database.
	 */
	private String url;

	/**
	 * Login username of the database.
	 */
	private String username;

	/**
	 * Login password of the database.
	 */
	private String password;
	

很清晰对应配置spring.datasource,然后是连接池配置:

@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class }):导入其他额外的配置,就以DataSourcePoolMetadataProvidersConfiguration为例吧, DataSourcePoolMetadataProvidersConfiguration是数据库连接池提供者的一个配置类,即Classpath中存在 org.apache.tomcat.jdbc.pool.DataSource.class,则使用tomcat-jdbc连接池,如果Classpath中存在 HikariDataSource.class则使用Hikari连接池。

# 外部化配置

  • 配置文件格式与加载顺序
  • Profile 切换
  • @ConfigurationProperties vs @Value
  • 配置源优先级(命令行 > 环境变量 > 配置文件 > defaults)
1、开发者工具 `Devtools` 全局配置参数;

2、单元测试上的 `@TestPropertySource` 注解指定的参数;

3、单元测试上的 `@SpringBootTest` 注解指定的参数;

4、命令行指定的参数,如 `java -jar springboot.jar --name="Java技术栈"`;

5、命令行中的 `SPRING_APPLICATION_JSONJSON` 指定参数, 如 `java -Dspring.application.json='{"name":"Java技术栈"}' -jar springboot.jar`

6、`ServletConfig` 初始化参数;

7、`ServletContext` 初始化参数;

8、JNDI参数(如 `java:comp/env/spring.application.json`);

9、Java系统参数(来源:`System.getProperties()`);

10、操作系统环境变量参数;

11、`RandomValuePropertySource` 随机数,仅匹配:`ramdom.*`;

12、JAR包外面的配置文件参数(`application-{profile}.properties(YAML)`)

13、JAR包里面的配置文件参数(`application-{profile}.properties(YAML)`)

14、JAR包外面的配置文件参数(`application.properties(YAML)`)

15、JAR包里面的配置文件参数(`application.properties(YAML)`)

16、`@Configuration`配置文件上 `@PropertySource` 注解加载的参数;

17、默认参数(通过 `SpringApplication.setDefaultProperties` 指定);

# 生产级特性

  • Actuator 端点
  • DevTools 热部署原理

# Spring Boot应用监控与调优方案

生产环境监控:使用APM工具(如SkyWalking、Pinpoint)

开发阶段:集成Spring Boot DevTools进行热加载监控

整体监控架构

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   应用层监控      │    │   JVM层监控      │    │   系统层监控      │
│                 │    │                 │    │                 │
│ • 启动时间监控    │    │ • 内存使用       │    │ • CPU使用率      │
│ • Bean加载监控   │    │ • GC统计        │    │ • 磁盘IO         │
│ • 请求响应监控    │    │ • 线程状态       │    │ • 网络流量       │
└─────────┬───────┘    └─────────┬───────┘    └─────────┬───────┘
          │                      │                      │
          └──────────────────────┼──────────────────────┘
                                 │
                    ┌─────────────▼─────────────┐
                    │     监控数据收集层          │
                    │                           │
                    │ • Micrometer              │
                    │ • Spring Boot Actuator    │
                    │ • 自定义监控组件           │
                    └─────────────┬─────────────┘
                                 │
                    ┌─────────────▼─────────────┐
                    │     数据存储与可视化        │
                    │                           │
                    │ • Prometheus + Grafana    │
                    │ • ELK Stack               │
                    │ • 时序数据库              │
                    └───────────────────────────┘

# 依赖配置

<!-- Spring Boot Actuator -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<!-- Micrometer Prometheus -->
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

<!-- 性能监控 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<!-- 日志监控 -->
<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>7.2</version>
</dependency>

# 启动过程专项监控

@Component
public class StartupMonitor {
    private static final Logger logger = LoggerFactory.getLogger(StartupMonitor.class);
    
    private long appStartTime;
    private Map<String, Long> phaseTimestamps = new ConcurrentHashMap<>();
    
    @EventListener
    public void handleApplicationStarting(ApplicationStartingEvent event) {
        appStartTime = System.currentTimeMillis();
        phaseTimestamps.put("starting", appStartTime);
        logger.info("应用开始启动: {}", appStartTime);
    }
    
    @EventListener
    public void handleEnvironmentPrepared(ApplicationEnvironmentPreparedEvent event) {
        long time = System.currentTimeMillis();
        phaseTimestamps.put("environmentPrepared", time);
        logger.info("环境准备完成, 耗时: {}ms", time - appStartTime);
        
        // 记录环境配置信息
        Environment env = event.getEnvironment();
        logger.info("Active Profiles: {}", Arrays.toString(env.getActiveProfiles()));
        logger.info("Config Locations: {}", env.getProperty("spring.config.location", "default"));
    }
    
    @EventListener
    public void handleContextPrepared(ApplicationPreparedEvent event) {
        long time = System.currentTimeMillis();
        phaseTimestamps.put("contextPrepared", time);
        logger.info("上下文准备完成, 总耗时: {}ms", time - appStartTime);
        
        // 记录Bean定义数量
        ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
        int beanDefinitionCount = beanFactory.getBeanDefinitionCount();
        logger.info("Bean定义数量: {}", beanDefinitionCount);
    }
    
    @EventListener
    public void handleContextRefreshed(ContextRefreshedEvent event) {
        long time = System.currentTimeMillis();
        phaseTimestamps.put("contextRefreshed", time);
        
        ApplicationContext context = event.getApplicationContext();
        String[] beanNames = context.getBeanDefinitionNames();
        
        logger.info("容器刷新完成, Bean实例化数量: {}, 总耗时: {}ms", 
                   beanNames.length, time - appStartTime);
        
        // 分析Bean依赖关系
        analyzeBeanDependencies(context, beanNames);
        
        // 识别慢加载Bean
        identifySlowLoadingBeans();

        // 记录启动性能指标
        recordStartupMetrics();
    }
    
    @EventListener
    public void handleApplicationReady(ApplicationReadyEvent event) {
        long totalTime = System.currentTimeMillis() - appStartTime;
        logger.info("应用启动完成, 总耗时: {}ms", totalTime);
        
        // 发布启动性能报告
        publishStartupReport(totalTime);
    }
    
    private void analyzeBeanDependencies(ApplicationContext context, String[] beanNames) {
        logger.info("=== Bean依赖分析 ===");
        
        for (String beanName : beanNames) {
            if (beanName.contains("Service") || beanName.contains("Controller")) {
                BeanDefinition bd = ((DefaultListableBeanFactory) 
                    context.getAutowireCapableBeanFactory()).getBeanDefinition(beanName);
                
                if (bd.hasAttribute("dependsOn")) {
                    String[] dependsOn = (String[]) bd.getAttribute("dependsOn");
                    logger.info("Bean {} 依赖: {}", beanName, Arrays.toString(dependsOn));
                }
            }
        }
    }
    
    private void identifySlowLoadingBeans() {
        logger.info("=== 慢加载Bean识别 ===");
        
        phaseTimestamps.entrySet().stream()
            .filter(entry -> entry.getValue().getMaxTime() > 500) // 超过500ms
            .sorted((e1, e2) -> Long.compare(e2.getValue().getMaxTime(), 
                    e1.getValue().getMaxTime()))
            .forEach(entry -> {
                logger.warn("慢加载Bean: {}, 最大耗时: {}ms", 
                    entry.getKey(), entry.getValue().getMaxTime());
            });
    }

    private void recordStartupMetrics() {
        // 记录到Micrometer指标
        Metrics.gauge("application.startup.duration", 
            System.currentTimeMillis() - appStartTime);
        Metrics.gauge("application.beans.count", 
            phaseTimestamps.size());
    }
    
    private void publishStartupReport(long totalTime) {
        logger.info("=== Spring Boot启动性能报告 ===");
        logger.info("总启动时间: {}ms", totalTime);
        
        phaseTimestamps.entrySet().stream()
            .sorted(Map.Entry.comparingByValue())
            .forEach(entry -> {
                logger.info("阶段 {}: {}ms", entry.getKey(), entry.getValue() - appStartTime);
            });
    }
}

# Bean加载性能监控

@Component
public class BeanLoadingMonitor implements BeanPostProcessor {
    private final Map<String, Long> beanCreationStartTimes = new ConcurrentHashMap<>();
    private final Map<String, BeanMetrics> beanMetrics = new ConcurrentHashMap<>();
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        beanCreationStartTimes.put(beanName, System.currentTimeMillis());
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        Long startTime = beanCreationStartTimes.remove(beanName);
        if (startTime != null) {
            long duration = System.currentTimeMillis() - startTime;
            BeanMetrics metrics = beanMetrics.computeIfAbsent(beanName, 
                k -> new BeanMetrics(bean.getClass().getSimpleName()));
            metrics.recordCreation(duration);
            
            if (duration > 100) { // 超过100ms记录警告
                logger.warn("Bean [{}] 初始化耗时较长: {}ms", beanName, duration);
            }
        }
        return bean;
    }
    
    @EventListener
    public void handleApplicationReady(ApplicationReadyEvent event) {
        // 启动完成后输出Bean加载性能报告
        logger.info("=== Bean加载性能报告 ===");
        beanMetrics.entrySet().stream()
            .sorted((e1, e2) -> Long.compare(e2.getValue().getMaxTime(), e1.getValue().getMaxTime()))
            .limit(10) // 只显示最慢的10个Bean
            .forEach(entry -> {
                BeanMetrics metrics = entry.getValue();
                logger.info("Bean [{}]: 最大耗时={}ms, 平均耗时={}ms", 
                    entry.getKey(), metrics.getMaxTime(), metrics.getAverageTime());
            });
    }
    
    static class BeanMetrics {
        private final String className;
        private long totalTime;
        private long maxTime;
        private int count;
        
        BeanMetrics(String className) {
            this.className = className;
        }
        
        void recordCreation(long duration) {
            totalTime += duration;
            maxTime = Math.max(maxTime, duration);
            count++;
        }
        
        long getAverageTime() {
            return count > 0 ? totalTime / count : 0;
        }
        
        long getMaxTime() {
            return maxTime;
        }
    }
}

# 配置类加载监控

@Component
public class ConfigurationLoadMonitor implements BeanFactoryPostProcessor {
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        if (beanFactory instanceof DefaultListableBeanFactory) {
            DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
            
            // 监控配置类解析
            dlbf.addBeanPostProcessor(new BeanPostProcessor() {
                @Override
                public Object postProcessBeforeInitialization(Object bean, String beanName) {
                    if (beanName != null && beanName.endsWith("Configuration")) {
                        logger.debug("配置类加载: {}", beanName);
                    }
                    return bean;
                }
            });
        }
    }
}

# JVM级别监控配置

# application-monitor.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus,startup
  endpoint:
    metrics:
      enabled: true
    prometheus:
      enabled: true
  metrics:
    export:
      prometheus:
        enabled: true
    enable:
      jvm: true
      system: true
      logback: true
  tracing:
    sampling:
      probability: 1.0

# JVM监控配置
server:
  port: 8080
spring:
  application:
    name: my-monitored-app

# 自定义健康检查与指标

@Component
public class ApplicationHealthIndicator implements HealthIndicator {
    
    @Override
    public Health health() {
        // 检查应用健康状态
        boolean isHealthy = checkApplicationHealth();
        
        if (isHealthy) {
            return Health.up()
                .withDetail("启动时间", ManagementFactory.getRuntimeMXBean().getUptime() + "ms")
                .withDetail("内存使用", getMemoryUsage())
                .build();
        } else {
            return Health.down()
                .withDetail("错误原因", "应用运行异常")
                .build();
        }
    }
    
    private String getMemoryUsage() {
        Runtime runtime = Runtime.getRuntime();
        long usedMemory = runtime.totalMemory() - runtime.freeMemory();
        long maxMemory = runtime.maxMemory();
        return String.format("%.2fMB/%.2fMB", 
            usedMemory / 1024.0 / 1024.0, maxMemory / 1024.0 / 1024.0);
    }
}

@Component
public class CustomMetrics {
    private final MeterRegistry meterRegistry;
    private final Counter beanCreationCounter;
    private final Timer startupTimer;
    
    public CustomMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.beanCreationCounter = Counter.builder("application.beans.created")
            .description("应用创建的Bean数量")
            .register(meterRegistry);
            
        this.startupTimer = Timer.builder("application.startup.time")
            .description("应用启动时间")
            .register(meterRegistry);
    }
    
    public void recordBeanCreation() {
        beanCreationCounter.increment();
    }
    
    public void recordStartupTime(long duration) {
        startupTimer.record(duration, TimeUnit.MILLISECONDS);
    }
}

# 日志监控配置

<!-- logback-spring.xml -->
<configuration>
    <appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <customFields>{"application":"${spring.application.name}"}</customFields>
        </encoder>
    </appender>
    
    <appender name="METRICS" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/metrics.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/metrics.%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
    </appender>
    
    <logger name="com.example.monitor" level="DEBUG" additivity="false">
        <appender-ref ref="METRICS" />
        <appender-ref ref="JSON" />
    </logger>
    
    <root level="INFO">
        <appender-ref ref="JSON" />
    </root>
</configuration>

# Grafana监控面板配置

{
  "title": "Spring Boot应用监控",
  "panels": [
    {
      "title": "启动时间监控",
      "targets": [
        {
          "expr": "application_startup_time_seconds",
          "legendFormat": "启动耗时"
        }
      ],
      "type": "stat"
    },
    {
      "title": "Bean加载性能",
      "targets": [
        {
          "expr": "rate(application_beans_created_total[5m])",
          "legendFormat": "Bean创建速率"
        }
      ],
      "type": "graph"
    },
    {
      "title": "JVM内存使用",
      "targets": [
        {
          "expr": "jvm_memory_used_bytes{area=\"heap\"}",
          "legendFormat": "堆内存"
        }
      ],
      "type": "graph"
    }
  ]
}

# 使用springboot开发应用

Spring boot web default web server: Asp.net default is IIS Express, how about spring boot web? Tomcat vs. Jetty vs. Undertow: Comparison of Spring Boot Embedded Servlet Containers https://examples.javacodegeeks.com/enterprise-java/spring/tomcat-vs-jetty-vs-undertow-comparison-of-spring-boot-embedded-servlet-containers/

# 3.1 Plugins

** spring-boot-maven-plugin

<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<version>${spring-boot-maven-plugin.version}</version>
				<executions>
					<execution>
						<goals>
							<goal>repackage</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

这个plugin很重要,其中的repackage是把java程序打包成为executable程序,否则就只是普通的jar包(纠正:实际测试即使没用这个plugin,pom parent继承了org.springframework.boot,一样可以生成可执行程序,原因见后面关于这个包的解释),不能直接执行,比如hello world,需要javac之后生成的class,用java *.class才能执行,而且如果java程序比较负责,依赖了外部的包,还要给出classpath或libpath,极其麻烦,maven本身就是包管理器,最基本的职责就是mvn package打成普通的jar包,然后mvn install到本地.m2 repository,然后mvn deploy到远程的repository,当需要运行的时候就是用前面提到的java命令执行,当然也可以直接用mvn执行,好处是mvn会自动去.m2下面找到依赖的包, mvn exec:java -Dexec.mainClass="com.example.Main" -Dexec.args="arg0 arg1",可以看到mvn实际也就是调用java命令 首先使用这个plugin的情况下,正常的mvn clean package,生成XXX-0.0.1-SNAPSHOT.jar和XXX-0.0.1-SNAPSHOT.jar.origninal: 可以重命名一下orignial为jar,反编译对比下:

对比可以看到,普通的jar包里面的东西被再次包入了BOOT-INF,然后增加了一个org.springframework.boot.loader的启动包 执行方法: project里:mvn spring-boot:run 打成包后:java -server -jar XXXX.jar --spring.config.location=/config/

这里居然还有个比较傻逼的比较 https://www.baeldung.com/spring-boot-run-maven-vs-executable-jar 有点意思,还有人这么较真

然后我好奇测试了下mvn最原始的打包plugin,看打成一个fat jar会如何 无法运行,估计是缺少springboot的上下文,spring boot的程序自然真正的入口应该是spring boot那个loader,加了annotation的那个所谓的入口main实际只是为spring boot loader准备的入口;

** maven-enforcer-plugin 这个是用来检查依赖问题的 mvn enforcer:enforce

# 3.2 Dependencies

Parent org.springframework.boot https://www.baeldung.com/spring-boot-dependency-management-custom-parent

继承两种方式: 直接写在parent里面; 写在dependencymanagement里面

<dependencyManagement>
     <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

如果没有继承org.springframework.boot,如果有多个入口方法,在pom中指定:

<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<mainClass>com.lyhistory.rce.shiro.WebApp</mainClass>
				</configuration>
				<executions>
					<execution>
						<goals>
							<goal>repackage</goal>
						</goals>
					</execution>
				</executions>
			</plugin>

如果继承org.springframework.boot,如果有多个入口方法,就多一种方式,在pom中指定:

<parent>
   	 <groupId>org.springframework.boot</groupId>
   	 <artifactId>spring-boot-starter-parent</artifactId>
   	 <version>1.5.9.RELEASE</version>
   	 <relativePath /> <!-- lookup parent from repository -->
</parent>

<properties>
  <start-class>com.xx.xx</start-class>
</properties>

# 3.3 Integration

# 3.3.1 redis

@Autowired Private RedisTemplate redisTemplate; https://github.com/spring-projects/spring-boot/issues/7238

一文搞定 Spring Data Redis 详解及实战 https://cloud.tencent.com/developer/article/1349818 SpringBoot下Redis相关配置是如何被初始化的 https://my.oschina.net/u/3866531/blog/1858069

Which type of injection?? 深度解析SpringBoot2.x整合Spring-Data-Redis https://www.itcodemonkey.com/article/13627.html

# 3.3.2 Shiro

# more

Thymeleaf https://www.baeldung.com/thymeleaf-in-spring-mvc

# 4.0 项目技巧

# include

application.yml

application-datasource.yml

spring:
  profiles:
    #split into multiple profile files
    include: datasource

# multiple env: dev prod

application.properties

spring.profiles.active:@spring.profiles.active@

application-dev.yml

application-prod.yml

mvn spring-boot:run -Dspring.profiles.active=dev
mvn clean install -Dspring.profiles.active=dev

~pom.xml(不需要)~

<profiles>
        <profile>
            <id>dev</id>
            <properties>
                <myActiveProfile>dev</myActiveProfile>
            </properties>
        </profile>
        <profile>
            <id>prod</id>
            <properties>
                <myActiveProfile>prod</myActiveProfile>
            </properties>
        </profile>
    </profiles>

# 4.1 业务开发

# 4.1.1 使用starter

# POM depenedency

spring boot官方提供了很多现成的starter,可以直接引用其depdendency使用比如 starters (opens new window)

spring-boot-starter Core starter, including auto-configuration support, logging and YAML
spring-boot-starter-web Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container
spring-boot-starter-jdbc Starter for using JDBC with the HikariCP connection pool

但是问题是引用时需要加版本号,很多starter之间以及跟其他的dependency之间可能有版本依赖冲突, 所以官方推荐使用parent方式或者import方式引入某个版本的spring-boot-starter-parent,因为这个parent里面已经定义好了各个版本号, 所以在引用比如spring-boot-starter-web的时候就不需要添加版本号了

<!-- Inherit defaults from Spring Boot -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.6.RELEASE</version>
</parent>

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>
</dependencyManagement>

<!--The spring-boot-starter-parent POM includes <executions> configuration to bind the repackage goal.-->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

<!-- Dependence Override 
full list: https://github.com/spring-projects/spring-boot/blob/v2.2.6.RELEASE/spring-boot-project/spring-boot-dependencies/pom.xml
-->
<properties>
    <spring-data-releasetrain.version>Fowler-SR2</spring-data-releasetrain.version>
</properties>

# Configuration

默认是 application.properties,也可以使用yaml

都在这里:

https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html

另外一种稍微硬核的通过查阅代码获取的方式:

所有的配置都可以在 spring-boot-autoconfigure里面找到,比如

org.springframework.boot.autoconfigure.data.redis/RedisProperties

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {

	/**
	 * Database index used by the connection factory.
	 */
	private int database = 0;

	/**
	 * Connection URL. Overrides host, port, and password. User is ignored. Example:
	 * redis://user:[email protected]:6379
	 */
	private String url;

	/**
	 * Redis server host.
	 */
	private String host = "localhost";

	/**
	 * Login password of the redis server.
	 */
	private String password;

	/**
	 * Redis server port.
	 */
	private int port = 6379;

	/**
	 * Whether to enable SSL support.
	 */
	private boolean ssl;

	/**
	 * Connection timeout.
	 */
	private Duration timeout;

	private Sentinel sentinel;

	private Cluster cluster;

	private final Jedis jedis = new Jedis();

	private final Lettuce lettuce = new Lettuce();

	public int getDatabase() {
		return this.database;
	}

	public void setDatabase(int database) {
		this.database = database;
	}

	public String getUrl() {
		return this.url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getHost() {
		return this.host;
	}

	public void setHost(String host) {
		this.host = host;
	}

	public String getPassword() {
		return this.password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public int getPort() {
		return this.port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public boolean isSsl() {
		return this.ssl;
	}

	public void setSsl(boolean ssl) {
		this.ssl = ssl;
	}

	public void setTimeout(Duration timeout) {
		this.timeout = timeout;
	}

	public Duration getTimeout() {
		return this.timeout;
	}

	public Sentinel getSentinel() {
		return this.sentinel;
	}

	public void setSentinel(Sentinel sentinel) {
		this.sentinel = sentinel;
	}

	public Cluster getCluster() {
		return this.cluster;
	}

	public void setCluster(Cluster cluster) {
		this.cluster = cluster;
	}

	public Jedis getJedis() {
		return this.jedis;
	}

	public Lettuce getLettuce() {
		return this.lettuce;
	}

	/**
	 * Pool properties.
	 */
	public static class Pool {

		/**
		 * Maximum number of "idle" connections in the pool. Use a negative value to
		 * indicate an unlimited number of idle connections.
		 */
		private int maxIdle = 8;

		/**
		 * Target for the minimum number of idle connections to maintain in the pool. This
		 * setting only has an effect if it is positive.
		 */
		private int minIdle = 0;

		/**
		 * Maximum number of connections that can be allocated by the pool at a given
		 * time. Use a negative value for no limit.
		 */
		private int maxActive = 8;

		/**
		 * Maximum amount of time a connection allocation should block before throwing an
		 * exception when the pool is exhausted. Use a negative value to block
		 * indefinitely.
		 */
		private Duration maxWait = Duration.ofMillis(-1);

		public int getMaxIdle() {
			return this.maxIdle;
		}

		public void setMaxIdle(int maxIdle) {
			this.maxIdle = maxIdle;
		}

		public int getMinIdle() {
			return this.minIdle;
		}

		public void setMinIdle(int minIdle) {
			this.minIdle = minIdle;
		}

		public int getMaxActive() {
			return this.maxActive;
		}

		public void setMaxActive(int maxActive) {
			this.maxActive = maxActive;
		}

		public Duration getMaxWait() {
			return this.maxWait;
		}

		public void setMaxWait(Duration maxWait) {
			this.maxWait = maxWait;
		}

	}

	/**
	 * Cluster properties.
	 */
	public static class Cluster {

		/**
		 * Comma-separated list of "host:port" pairs to bootstrap from. This represents an
		 * "initial" list of cluster nodes and is required to have at least one entry.
		 */
		private List<String> nodes;

		/**
		 * Maximum number of redirects to follow when executing commands across the
		 * cluster.
		 */
		private Integer maxRedirects;

		public List<String> getNodes() {
			return this.nodes;
		}

		public void setNodes(List<String> nodes) {
			this.nodes = nodes;
		}

		public Integer getMaxRedirects() {
			return this.maxRedirects;
		}

		public void setMaxRedirects(Integer maxRedirects) {
			this.maxRedirects = maxRedirects;
		}

	}
    ..........
        /**
	 * Lettuce client properties.
	 */
	public static class Lettuce {

		/**
		 * Shutdown timeout.
		 */
		private Duration shutdownTimeout = Duration.ofMillis(100);

		/**
		 * Lettuce pool configuration.
		 */
		private Pool pool;

		public Duration getShutdownTimeout() {
			return this.shutdownTimeout;
		}

		public void setShutdownTimeout(Duration shutdownTimeout) {
			this.shutdownTimeout = shutdownTimeout;
		}

		public Pool getPool() {
			return this.pool;
		}

		public void setPool(Pool pool) {
			this.pool = pool;
		}

	}

}

可以看到前缀是 spring.redis ,具体配置举例:

  • 其中最简单的string类型

    spring.redis.host=x.x.x.48
    spring.redis.port=6379
    
  • 复杂类型如private Cluster cluster,很简单,进去看Cluster的成员即可,只是注意maxRedirects在application.properties写作:

    spring.redis.cluster.nodes=x.x.x.48:6379,x.x.x.48:6380,x.x.x.48:6381,x.x.x.49:6379,x.x.x.49:6380,x.x.x.49:6381,x.x.x.50:6379,x.x.x.50:6380,x.x.x.50:6381
    spring.redis.cluster.max-redirects=3
    
  • 使用yaml

    spring:
      #Redis缓存配置(RedisProperties)
      redis:
    #    database: 0
    #    host: localhost
    #    port: 6380
    #    password:
        #timeout: 6000
        #redis cluster
        cluster:
          nodes: 1.1.1.1:6379,1.1.1.1:6380,1.1.1.1:6381
          maxRedirects: 3
        lettuce:
          pool:
            max-active: 200
            max-wait: 3000
            max-idle: -1
            min-idle: 10
    
# 案例分析:

某次项目使用 spring-boot-starter版本为2.0.5

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath />
    </parent>

使用其 spring-boot-starter-data-redis 遇到RedisExcpetion 后来没能重现,不过也能重现出类似的WARN级别信息:

l.c.c.t.ClusterTopologyRefresh^[[m : Unable to connect to xxxx:6379

java.util.concurrent.CompletionException: io.netty.channel.ConnectTimeoutException: connection timed out: /xxxx:6379

经过查阅,client端的RedisConnectionFactory需要增加 ClusterTopologyRefreshOptions 这个option,

    @Autowired
    private RedisProperties redisProperties;
 
    @Value("${redis.maxRedirects:3}")
    private int maxRedirects;
 
    @Value("${redis.refreshTime:5}")
    private int refreshTime;
 
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
 
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(redisProperties.getCluster().getNodes());
 
        redisClusterConfiguration.setMaxRedirects(maxRedirects);
 
        / / Support adaptive cluster topology refresh and static refresh source
        ClusterTopologyRefreshOptions clusterTopologyRefreshOptions =  ClusterTopologyRefreshOptions.builder()
                .enablePeriodicRefresh()
                .enableAllAdaptiveRefreshTriggers()
                .refreshPeriod(Duration.ofSeconds(refreshTime))
                .build();
 
        ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
                .topologyRefreshOptions(clusterTopologyRefreshOptions).build();
 
                 / / From the priority, read and write separation, read from the possible inconsistency, the final consistency CP
        LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder()
                .readFrom(ReadFrom.SLAVE_PREFERRED)
                .clientOptions(clusterClientOptions).build();
 
        return new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
    }

而实际上为什么这个不提供配置呢,查阅了前面说的官方配置:

https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html

发现实际有这个选项

spring.redis.lettuce.cluster.refresh.dynamic-refresh-sources
spring.redis.lettuce.cluster.refresh.period

而进一步看到第一个config是在spring boot 2.4.0引入的

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.4.0-Configuration-Changelog

而第二个period是2.3.0

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3.0-Configuration-Changelog

直接搜源码 ClusterTopologyRefreshOptions 确认下:

https://github.com/spring-projects/spring-boot/blob/47516b50c39bd6ea924a1f6720ce6d4a71088651/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java

点击blame,找到这行

			if (refreshProperties.getPeriod() != null) {
				refreshBuilder.enablePeriodicRefresh(refreshProperties.getPeriod());
			}

点击左侧对应的提交:

https://github.com/spring-projects/spring-boot/commit/dfac3a282b98bd480c5acf778dbfbce994051dad

可以看到这次提交的comment:Add configuration to enable Redis Cluster topology refresh

然后从左上角可以看到是从 v2.3.0.M4 (opens new window) 最开始引入的,之后是 v2.3.0.RC1,v2.3.0.RELEASE,直到最新的v2.5.0-RC1


REFERENCE:

https://github.com/javastacks/spring-boot-best-practice https://github.com/YunaiV/SpringBoot-Labs

给你一份超详细 Spring Boot 知识清单 (opens new window)

一站式starter (opens new window)

About AutoConfig (opens new window) Gradually Replacing Auto-configuration Disabling Specific Auto-configuration Classes (exclude={DataSourceAutoConfiguration.class})

spring boot 中的 Parent POM 和 Starter 的作用什么 https://cloud.tencent.com/developer/article/1362790

EnvironmentPostProcessor BeanPostProcessor

# 4.1.2 springboot mvc

so by default, tomcat will create some temp folder under /tmp and use it later on

spring.servlet.multipart.location (opens new window)

https://stackoverflow.com/questions/50523407/the-temporary-upload-location-tmp-tomcat-4296537502689403143-5000-work-tomcat/50523578

# 4.2 框架开发(starter)

ConfigurationProperties和EnableConfigurationProperties是一对, 前者定义对应配置文件的属性,后者是激活读取; 同样, Configuration或AutoConfiguration跟EnableAutoConfiguration是一对, 前者相当于component就是定义为bean,而后者是激活这个自动配置/装配(具体参考springboot源码分析一文),

https://docs.spring.io/spring-boot/docs/1.3.8.RELEASE/reference/html/using-boot-auto-configuration.html

Create a Custom Auto-Configuration with Spring Boot

https://www.baeldung.com/spring-boot-custom-auto-configuration

创建一个starter hello-spring-boot-starter

写一个服务类
public class HelloService {

  private String msg;

  public String sayHello() {
    return "hello " + msg;
  }

  public String getMsg() {
    return msg;
  }

  public void setMsg(String msg) {
    this.msg = msg;
  }
}
@ConfigurationProperties(prefix = "hello") //获取属性值
public class HelloProperties {

  private static final String MSG = "world";

  private String msg = MSG ;

  public String getMsg() {
    return msg;
  }

  public void setMsg(String msg) {
    this.msg = msg;
  }

}
@Configuration
//为带有@ConfigurationProperties注解的Bean提供有效的支持。
// 这个注解可以提供一种方便的方式来将带有@ConfigurationProperties注解的类注入为Spring容器的Bean。
@EnableConfigurationProperties(HelloProperties.class)//开启属性注入,通过@autowired注入
@ConditionalOnClass(Hello.class)//判断这个类是否在classpath中存在,如果存在,才会实例化一个Bean
// The Hello bean will be created if the hello.enable property exists and has a value other than false
// or the property doesn't exist at all.
@ConditionalOnProperty(prefix="hello", value="enabled", matchIfMissing = true)
public class HelloAutoConfiguration {

  @Autowired
  private HelloProperties helloProperties;

  @Bean
  @ConditionalOnMissingBean(Hello.class)//容器中如果没有Hello这个类,那么自动配置这个Hello
  public HelloService hello() {
    HelloService hello = new HelloService();
    hello.setMsg(helloProperties.getMsg());
    return hello;
  }

}

application.properties
\#可以不配置
hello.enabled=true

hello.msg=charmingfst

\#以debug模式运行
debug=true

\src\main\resources\META-INF\spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.chm.test.HelloAutoConfiguration

https://blog.csdn.net/zxc123e/article/details/80222967

# 4.3 AOP切面编程

Aspect(切面)横切关注点的模块化 @Aspect注解的类

Join Point(连接点)程序执行过程中的点 方法调用、异常抛出

Pointcut(切点)匹配连接点的表达式 execution(* UserService.*(..))

Advice(通知)在连接点执行的动作 @Before, @After, @Around

Target(目标)被代理的对象 UserService实例

Proxy(代理)AOP创建的包装对象 CGLIB或JDK动态代理

Weaving(织入)将切面应用到目标对象的过程 编译期、类加载期、运行期

# 什么是AOP

传统OOP的问题:

// 业务类中混杂着横切关注点
public class UserService {
    public void createUser(User user) {
        // 事务开始
        Transaction tx = beginTransaction();
        try {
            // 权限检查
            checkPermission();
            
            // 日志记录
            log.info("创建用户: {}", user.getName());
            
            // 业务逻辑
            userDao.save(user);
            
            // 事务提交
            tx.commit();
        } catch (Exception e) {
            // 事务回滚
            tx.rollback();
            // 错误日志
            log.error("创建用户失败", e);
            throw e;
        }
    }
}

AOP解决方案:

// 纯净的业务逻辑
public class UserService {
    public void createUser(User user) {
        userDao.save(user); // 只关注业务逻辑
    }
}

// 横切关注点通过AOP处理
@Aspect
@Component
public class TransactionAspect {
    @Around("execution(* UserService.*(..))")
    public Object manageTransaction(ProceedingJoinPoint pjp) {
        // 事务管理逻辑在这里统一处理
    }
}

# Spring AOP实现机制-spring-boot-starter-aop

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

// 对于每个Bean:
1. Bean正常实例化、依赖注入、初始化
2. AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization()被调用
3. 检查Bean是否需要AOP代理
4. 如果需要,创建JDK动态代理或CGLIB代理
5. 返回代理对象(替代原始Bean)

// Spring框架真实的AOP代理创建类结构:
Object
└── BeanPostProcessor (接口)
    └── InstantiationAwareBeanPostProcessor (接口)  
        └── SmartInstantiationAwareBeanPostProcessor (接口)
            └── AbstractAutoProxyCreator (抽象类) ← 核心实现
                └── AspectJAwareAdvisorAutoProxyCreator (抽象类)
                    └── AnnotationAwareAspectJAutoProxyCreator (具体类) ← 实际工作的类!

这是Spring AOP的核心引擎

// 真实的Spring源码(简化版)
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
    implements SmartInstantiationAwareBeanPostProcessor {
    
    // 核心方法:在Bean初始化后创建代理
    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            
            // 检查是否已经处理过(避免循环)
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                // 包装Bean(如果需要代理)
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
    
    // 核心方法:决定是否创建代理
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 1. 检查是否应该跳过代理
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean, beanName)) {
            return bean;
        }
        
        // 2. 获取适用的Advisors(切面逻辑)
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        
        // 3. 如果需要代理,创建代理对象
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            return createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        }
        
        return bean;
    }
}

这是Spring Boot默认使用的具体实现

// 真实的Spring Boot AOP处理器
public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
    
    // 重写方法:支持@AspectJ注解风格的切面
    @Override
    protected List<Advisor> findCandidateAdvisors() {
        // 调用父类方法查找基于配置的Advisor
        List<Advisor> advisors = super.findCandidateAdvisors();
        
        // 添加基于@Aspect注解的Advisor
        if (this.aspectJAdvisorsBuilder != null) {
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }
        return advisors;
    }
    
    // 重写方法:支持JDK动态代理和CGLIB代理
    @Override
    protected boolean shouldProxyTargetClass(ConfigurableListableBeanFactory beanFactory, String beanName) {
        // 检查@EnableAspectJAutoProxy配置
        return (this.beanFactory != null && 
                this.beanFactory.getBeanDefinition(beanName).isProxyTargetClass());
    }
}

# 示例1:性能监控AOP

@Aspect
@Component
public class PerformanceMonitoringAspect {
    private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitoringAspect.class);
    
    // 监控Service层方法性能
    @Around("execution(* com.example.service.*.*(..))")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        
        long startTime = System.currentTimeMillis();
        try {
            // 执行原始方法
            Object result = joinPoint.proceed();
            long executionTime = System.currentTimeMillis() - startTime;
            
            // 记录性能指标
            if (executionTime > 100) { // 超过100ms记录警告
                logger.warn("方法执行缓慢: {}.{}, 耗时: {}ms", 
                    className, methodName, executionTime);
            }
            
            // 记录到监控系统
            Metrics.timer("service.method.duration")
                .tag("class", className)
                .tag("method", methodName)
                .record(executionTime, TimeUnit.MILLISECONDS);
            
            return result;
            
        } catch (Exception e) {
            long executionTime = System.currentTimeMillis() - startTime;
            logger.error("方法执行异常: {}.{}, 耗时: {}ms", 
                className, methodName, executionTime, e);
            throw e;
        }
    }
}

# 示例2:事务管理AOP

@Aspect
@Component
public class TransactionAspect {
    
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    // 为所有@Transactional方法添加事务管理
    @Around("@annotation(transactional)")
    public Object manageTransaction(ProceedingJoinPoint joinPoint, 
                                   Transactional transactional) throws Throwable {
        
        TransactionDefinition definition = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(definition);
        
        try {
            logger.debug("开始事务: {}", joinPoint.getSignature());
            
            // 执行业务方法
            Object result = joinPoint.proceed();
            
            // 提交事务
            transactionManager.commit(status);
            logger.debug("提交事务: {}", joinPoint.getSignature());
            
            return result;
            
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(status);
            logger.debug("回滚事务: {}, 原因: {}", joinPoint.getSignature(), e.getMessage());
            throw e;
        }
    }
    
    // 使用示例
    @Service
    public class UserService {
        @Transactional
        public void createUser(User user) {
            // 业务逻辑,自动享有事务管理
            userRepository.save(user);
            userAuditRepository.logCreation(user);
        }
    }
}

# 示例3:缓存AOP

@Aspect
@Component
public class CacheAspect {
    
    @Autowired
    private CacheManager cacheManager;
    
    // 方法缓存
    @Around("@annotation(cacheable)")
    public Object handleCacheable(ProceedingJoinPoint joinPoint, 
                                Cacheable cacheable) throws Throwable {
        
        String cacheName = cacheable.value();
        String key = generateCacheKey(joinPoint);
        
        Cache cache = cacheManager.getCache(cacheName);
        ValueWrapper cachedValue = cache.get(key);
        
        if (cachedValue != null) {
            logger.debug("缓存命中: {}.{}", 
                joinPoint.getTarget().getClass().getSimpleName(),
                joinPoint.getSignature().getName());
            return cachedValue.get();
        }
        
        // 缓存未命中,执行方法
        Object result = joinPoint.proceed();
        
        // 缓存结果
        cache.put(key, result);
        logger.debug("缓存设置: {}.{}", 
            joinPoint.getTarget().getClass().getSimpleName(),
            joinPoint.getSignature().getName());
        
        return result;
    }
    
    // 缓存失效
    @After("@annotation(cacheEvict)")
    public void handleCacheEvict(JoinPoint joinPoint, CacheEvict cacheEvict) {
        String cacheName = cacheEvict.value();
        Cache cache = cacheManager.getCache(cacheName);
        
        if (cacheEvict.allEntries()) {
            cache.clear(); // 清空整个缓存
        } else {
            String key = generateCacheKey(joinPoint);
            cache.evict(key); // 清除特定key
        }
    }
    
    private String generateCacheKey(ProceedingJoinPoint joinPoint) {
        // 基于方法签名和参数生成缓存key
        return joinPoint.getSignature().toShortString() + 
               Arrays.toString(joinPoint.getArgs());
    }
}

# 示例4:安全权限AOP

@Aspect
@Component
public class SecurityAspect {
    
    @Autowired
    private AuthenticationService authService;
    
    // 方法级权限控制
    @Around("@annotation(requiresPermission)")
    public Object checkPermission(ProceedingJoinPoint joinPoint,
                                RequiresPermission requiresPermission) throws Throwable {
        
        String permission = requiresPermission.value();
        User currentUser = authService.getCurrentUser();
        
        if (!authService.hasPermission(currentUser, permission)) {
            throw new AccessDeniedException(
                "用户 " + currentUser.getUsername() + " 没有权限: " + permission);
        }
        
        logger.info("权限检查通过: {} 执行 {}.{}", 
            currentUser.getUsername(),
            joinPoint.getTarget().getClass().getSimpleName(),
            joinPoint.getSignature().getName());
        
        return joinPoint.proceed();
    }
    
    // 参数级权限控制
    @Before("execution(* delete*(..)) && args(id,..)")
    public void checkDeletePermission(JoinPoint joinPoint, Long id) {
        User currentUser = authService.getCurrentUser();
        if (!authService.canDelete(currentUser, id)) {
            throw new AccessDeniedException("无权删除资源: " + id);
        }
    }
}

# 示例5:日志记录AOP

@Aspect
@Component
public class LoggingAspect {
    
    // 方法调用日志
    @Before("execution(* com.example.controller.*.*(..))")
    public void logMethodEntry(JoinPoint joinPoint) {
        logger.info("→ 进入方法: {}.{}", 
            joinPoint.getTarget().getClass().getSimpleName(),
            joinPoint.getSignature().getName());
        
        if (logger.isDebugEnabled()) {
            logger.debug("方法参数: {}", Arrays.toString(joinPoint.getArgs()));
        }
    }
    
    // 方法退出日志
    @AfterReturning(pointcut = "execution(* com.example.controller.*.*(..))", 
                   returning = "result")
    public void logMethodExit(JoinPoint joinPoint, Object result) {
        logger.info("← 退出方法: {}.{}", 
            joinPoint.getTarget().getClass().getSimpleName(),
            joinPoint.getSignature().getName());
        
        if (logger.isDebugEnabled() && result != null) {
            logger.debug("方法返回值: {}", result.toString());
        }
    }
    
    // 异常日志
    @AfterThrowing(pointcut = "execution(* com.example..*.*(..))", 
                  throwing = "ex")
    public void logException(JoinPoint joinPoint, Exception ex) {
        logger.error("✗ 方法执行异常: {}.{}", 
            joinPoint.getTarget().getClass().getSimpleName(),
            joinPoint.getSignature().getName(), ex);
    }
}

# AOP最佳实践

  1. 切点表达式优化
@Aspect
@Component
public class OptimizedPointcutAspect {
    
    // 不好的写法:过于宽泛
    // @Around("execution(* com.example..*(..))")
    
    // 好的写法:精确匹配
    @Around("execution(* com.example.service.*Service.*(..))")
    public Object serviceLayerMonitoring(ProceedingJoinPoint pjp) throws Throwable {
        // 只监控Service层
        return pjp.proceed();
    }
    
    // 使用注解驱动更精确
    @Around("@annotation(com.example.annotation.Monitored)")
    public Object monitoredMethods(ProceedingJoinPoint pjp) throws Throwable {
        // 只监控有@Monitored注解的方法
        return pjp.proceed();
    }
}
  1. 性能考虑
@Aspect
@Component
public class PerformanceOptimizedAspect {
    
    // 避免在切面中执行耗时操作
    @Around("execution(* com.example..*(..))")
    public Object optimizedAdvice(ProceedingJoinPoint pjp) throws Throwable {
        // 不好的做法:每次都生成复杂日志
        // String detailedLog = createDetailedLog(pjp); // 耗时操作
        
        // 好的做法:延迟计算
        if (logger.isDebugEnabled()) {
            logger.debug("执行方法: {}", pjp.getSignature());
        }
        
        return pjp.proceed();
    }
}

# 5. Spring Boot启动优化

启动性能监控和分析

使用Spring Boot Actuator监控启动时间

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: startup
  endpoint:
    startup:
      enabled: true

spring:
  application:
    name: myapp

// 启动时间监控端点
@RestController
public class StartupMetricsController {
    
    @Autowired
    private ApplicationStartup startup;
    
    @GetMapping("/startup-metrics")
    public StartupMetrics getStartupMetrics() {
        return startup.getMetrics();
    }
}

// 启动时间分析结果示例
public class StartupAnalysis {
    public void analyzeStartupTime() {
        System.out.println("启动阶段耗时分析:");
        System.out.println("JVM启动: 0.8秒");
        System.out.println("组件扫描: 4.2秒 (51%) ← 优化重点!");
        System.out.println("Bean实例化: 2.1秒 (26%)");
        System.out.println("自动配置: 1.1秒 (13%)");
        System.out.println("服务器启动: 0.3秒 (4%)");
        System.out.println("总启动时间: 8.5秒");
    }
}

使用JVM参数优化启动性能

# 优化启动速度的JVM参数
java -jar myapp.jar \
  -Xms512m -Xmx512m \          # 固定堆大小,避免动态调整
  -XX:+UseG1GC \               # 使用G1垃圾回收器
  -XX:MaxGCPauseMillis=200 \   # 控制GC停顿时间
  -noverify \                  # 关闭字节码验证(开发环境)
  -Dspring.jmx.enabled=false \ # 关闭JMX监控
  -Dspring.main.lazy-initialization=true \ # 启用懒加载
  -Dspring.context.index.enabled=true      # 启用类路径索引

案例1:减少组件扫描范围

优化前(扫描整个包路径):
@SpringBootApplication
@ComponentScan("com.example")  // 扫描整个项目包
public class MyApplication {
    // 启动时间: 8.2秒(扫描2000个类)
}

优化后(精确扫描必要包):
@SpringBootApplication
@ComponentScan({
    "com.example.controller",
    "com.example.service", 
    "com.example.config"
    // 不扫描: com.example.entity, com.example.dto等
})
public class MyApplication {
    // 启动时间: 3.1秒(只扫描500个类)
}

案例2:使用懒加载优化

优化前(所有Bean立即加载):
@Service
public class HeavyService {
    // 启动时立即初始化,耗时2秒
    public HeavyService() {
        loadLargeDataSet();  // 耗时的初始化操作
    }
}
优化后(按需懒加载):
@Service
@Lazy  // 添加懒加载注解
public class HeavyService {
    // 第一次使用时才初始化
    public HeavyService() {
        loadLargeDataSet();
    }
}

@RestController
public class MyController {
    @Autowired
    private HeavyService heavyService;  // 启动时不初始化
    
    @GetMapping("/data")
    public Data getData() {
        // 第一次调用时才初始化HeavyService
        return heavyService.processData();
    }
}

案例3:排除不必要的自动配置

优化前(加载所有自动配置):
@SpringBootApplication  // 默认加载100+个自动配置
public class MyApplication {
    // 启动时间: 6.5秒
}

优化后(排除不需要的配置):
@SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class,      // 不使用数据库
    SecurityAutoConfiguration.class,        // 不需要安全配置
    MailSenderAutoConfiguration.class,     // 不需要邮件发送
    CacheAutoConfiguration.class           // 不使用缓存
})
public class MyApplication {
    // 启动时间: 3.8秒
}

案例4:类路径索引优化

优化前(类路径扫描):
// Maven依赖包含大量不需要的jar
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId> <!-- 不用数据库 -->
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-security</artifactId> <!-- 不用安全 -->
    </dependency>
</dependencies>

优化后(精简依赖):
// 只引入必要的starter
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 移除不必要的starter -->
</dependencies>

// 添加类路径索引(Spring 5.2+)
spring:
  context:
    index:
      enabled: true  # 启用编译时类路径索引

案例5:使用Spring Fu进行函数式配置

传统配置方式:
@Configuration
public class TraditionalConfig {
    @Bean
    public MyService myService() {
        return new MyService();  // 反射创建,性能一般
    }
}

函数式配置(Spring Fu):
public class FunctionalConfig {
    public static void main(String[] args) {
        var app = new SpringApplication(Application.class)
            .run(args);
        
        // 函数式注册Bean,避免反射开销
        app.addInitializer((context) -> {
            context.registerBean(MyService.class, 
                () -> new MyService());  // 直接lambda创建
        });
    }
}

案例6:编译时处理优化

使用Micronaut或Quarkus的编译时处理:
// Quarkus示例:编译时处理依赖注入
@ApplicationScoped
public class MyService {
    @Inject
    MyRepository repository;  // 编译时生成注入代码,避免运行时反射
}


# 6. Troubleshooting

# BeanDefinitionOverrideException

https://www.baeldung.com/spring-boot-bean-definition-override-exception

# BeanCurrentlyInCreationException/circular reference

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'AAAA': Bean with name 'AAAA' has been injected into other beans [BBBB] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.

解决:加@Lazy

# nohup: ignoring input no main manifest attribute

执行 
nohup java -server -jar test.jar > `date +\%F`_funding-rate-datasource_`date +\%H.%M.%S`.log 2>&1
log中内容为:
nohup: ignoring input no main manifest attribute
解决方法,pom增加:
<build>
        <finalName>funding-rate-datasource</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <configuration>
                    <attach>true</attach>
                </configuration>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!-- package -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>
            <!-- deploy -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-deploy-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>

ref:

@bean方法调用的特殊性:

https://www.racecoder.com/archives/787/

https://stackoverflow.com/questions/27990060/calling-a-bean-annotated-method-in-spring-java-configuration

Spring Boot 2.0 :深入分析Spring Boot原理 (opens new window)

spring boot之自动装配(spring-boot-autoconfigure) https://blog.csdn.net/wangjie5540/article/details/99542777

原创 | 我被面试官给虐懵了,竟然是因为我不懂Spring中的@Configuration https://juejin.im/post/5d005860f265da1b7f297630

https://blog.csdn.net/yiifaa/java/article/details/74852425 https://www.huaweicloud.com/articles/b59be8ffdcfbd1f8a1fe28bffe848d20.html https://www.cnblogs.com/wuchanming/p/5426746.html https://stackoverflow.com/questions/39890849/what-exactly-is-field-injection-and-how-to-avoid-it

https://medium.com/@ilyailin7777/all-dependency-injection-types-spring-336da7baf51b

SpringBoot常见异步编程 https://mp.weixin.qq.com/s/Z-GE_qBtnSgLQTVr9bQYcQ