Spring Boot 启动流程详解
一、知识概述
Spring Boot 应用的启动过程是一个复杂而精妙的过程,涉及类的加载、配置的读取、上下文的创建、Bean 的实例化等多个阶段。深入理解启动流程,有助于排查启动问题、优化启动性能,以及更好地理解 Spring Boot 的工作原理。
启动流程核心阶段:
- 准备阶段:环境准备、参数解析
- 创建阶段:应用上下文创建
- 配置阶段:环境配置、初始化器执行
- 刷新阶段:Bean 定义加载、Bean 实例化
- 完成阶段:事件发布、Runner 执行
二、知识点详细讲解
2.1 启动入口
Spring Boot 应用的标准启动方式:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
SpringApplication 构造过程
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 1. 设置资源加载器
this.resourceLoader = resourceLoader;
// 2. 断言主配置类不为空
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 3. 推断应用类型(Servlet/Reactive/None)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 4. 加载 BootstrapRegistryInitializer
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 5. 加载 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 6. 加载 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 7. 推断主类
this.mainApplicationClass = deduceMainApplicationClass();
}
2.2 启动流程概览
run()
↓
1. StopWatch 启动计时
↓
2. 配置 Headless 模式
↓
3. 获取 SpringApplicationRunListeners
↓
4. listeners.starting()
↓
5. 准备环境(Environment)
↓
6. 打印 Banner
↓
7. 创建 ApplicationContext
↓
8. 准备上下文
↓
9. 刷新上下文
↓
10. 刷新后处理
↓
11. listeners.started()
↓
12. 执行 Runners
↓
13. listeners.running()
2.3 应用类型推断
Spring Boot 根据类路径推断应用类型:
static WebApplicationType deduceFromClasspath() {
// REACTIVE:存在 DispatcherHandler 且不存在 DispatcherServlet
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// SERVLET:存在 Servlet 环境
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
2.4 事件监听机制
Spring Boot 在启动过程中发布一系列事件:
| 事件 | 触发时机 |
|---|---|
| ApplicationStartingEvent | 启动开始 |
| ApplicationEnvironmentPreparedEvent | 环境准备完成 |
| ApplicationContextInitializedEvent | 上下文初始化 |
| ApplicationPreparedEvent | 上下文准备完成 |
| ApplicationStartedEvent | 上下文已启动 |
| ApplicationReadyEvent | 应用就绪 |
| ApplicationFailedEvent | 启动失败 |
2.5 上下文创建
根据应用类型创建不同的上下文:
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
// 默认工厂实现
public static final String DEFAULT_CONTEXT_CLASS =
"org.springframework.context.annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS =
"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS =
"org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
2.6 Bean 定义加载
Bean 定义的来源:
- 主配置类:@SpringBootApplication 标注的类
- @ComponentScan:扫描的组件
- @Import:导入的配置类
- spring.factories:自动配置类
- @ImportResource:XML 配置文件
三、代码示例
3.1 启动流程详解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// 方式1:简单启动
// SpringApplication.run(Application.class, args);
// 方式2:自定义启动
SpringApplication app = new SpringApplication(Application.class);
// 自定义配置
app.setBannerMode(Banner.Mode.CONSOLE);
app.setAdditionalProfiles("dev");
app.setLazyInitialization(true);
app.setLogStartupInfo(true);
// 添加监听器
app.addListeners(new MyApplicationListener());
// 添加初始化器
app.addInitializers(new MyApplicationContextInitializer());
// 设置默认属性
Map<String, Object> defaultProps = new HashMap<>();
defaultProps.put("server.port", "8081");
app.setDefaultProperties(defaultProps);
// 启动应用
ConfigurableApplicationContext context = app.run(args);
// 获取环境信息
ConfigurableEnvironment env = context.getEnvironment();
System.out.println("Active Profiles: " + Arrays.toString(env.getActiveProfiles()));
System.out.println("Server Port: " + env.getProperty("server.port"));
}
}
3.2 自定义启动过程
import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.context.*;
import org.springframework.core.env.*;
import java.util.*;
public class CustomSpringApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(CustomSpringApplication.class);
// 1. 设置 Banner
application.setBanner((environment, sourceClass, out) -> {
out.println("========================================");
out.println(" Custom Spring Boot Application");
out.println(" Version: 1.0.0");
out.println("========================================");
});
application.setBannerMode(Banner.Mode.CONSOLE);
// 2. 设置应用类型
application.setWebApplicationType(WebApplicationType.SERVLET);
// 3. 设置初始化器
application.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("执行初始化器...");
// 注册自定义 Bean
applicationContext.getBeanFactory().registerSingleton("customBean", new CustomBean());
}
});
// 4. 设置监听器
application.addListeners(new ApplicationListener<ApplicationEvent>() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("收到事件: " + event.getClass().getSimpleName());
}
});
// 5. 设置属性源
Map<String, Object> properties = new HashMap<>();
properties.put("app.custom.property", "custom-value");
application.setDefaultProperties(properties);
// 6. 允许覆盖 Bean 定义
application.setAllowBeanDefinitionOverriding(true);
// 7. 设置懒加载
application.setLazyInitialization(true);
// 8. 运行应用
ConfigurableApplicationContext context = application.run(args);
// 9. 启动后处理
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("应用关闭中...");
context.close();
}));
}
}
class CustomBean {
public void sayHello() {
System.out.println("Hello from CustomBean!");
}
}
3.3 启动事件监听
import org.springframework.boot.context.event.*;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class StartupEventListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
System.out.println("🚀 [1] 应用开始启动...");
} else if (event instanceof ApplicationEnvironmentPreparedEvent) {
System.out.println("🌍 [2] 环境准备完成");
ApplicationEnvironmentPreparedEvent envEvent = (ApplicationEnvironmentPreparedEvent) event;
System.out.println(" 激活的 Profiles: " +
Arrays.toString(envEvent.getEnvironment().getActiveProfiles()));
} else if (event instanceof ApplicationContextInitializedEvent) {
System.out.println("📦 [3] 上下文初始化完成");
} else if (event instanceof ApplicationPreparedEvent) {
System.out.println("📋 [4] 上下文准备完成");
} else if (event instanceof ApplicationStartedEvent) {
System.out.println("▶️ [5] 应用已启动");
} else if (event instanceof ApplicationReadyEvent) {
System.out.println("✅ [6] 应用就绪,可以接收请求");
} else if (event instanceof ApplicationFailedEvent) {
System.out.println("❌ [!] 应用启动失败");
ApplicationFailedEvent failedEvent = (ApplicationFailedEvent) event;
failedEvent.getException().printStackTrace();
}
}
}
// 针对特定事件的监听器
@Component
public class ApplicationReadyEventListener
implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
ConfigurableApplicationContext context = event.getApplicationContext();
ConfigurableEnvironment env = context.getEnvironment();
System.out.println("\n========== 应用就绪 ==========");
System.out.println("应用名称: " + env.getProperty("spring.application.name"));
System.out.println("服务端口: " + env.getProperty("server.port"));
System.out.println("激活环境: " + Arrays.toString(env.getActiveProfiles()));
System.out.println("启动耗时: " + event.getTimeTaken().toMillis() + " ms");
System.out.println("==============================\n");
}
}
3.4 ApplicationContextInitializer
import org.springframework.context.*;
import org.springframework.beans.factory.config.*;
import org.springframework.stereotype.Component;
// 自定义初始化器
public class MyApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("执行 ApplicationContextInitializer...");
// 1. 注册自定义 BeanPostProcessor
applicationContext.getBeanFactory().addBeanPostProcessor(new CustomBeanPostProcessor());
// 2. 添加属性源
ConfigurableEnvironment env = applicationContext.getEnvironment();
Map<String, Object> customProps = new HashMap<>();
customProps.put("initializer.added", "true");
env.getPropertySources().addFirst(new MapPropertySource("customProps", customProps));
// 3. 注册单例 Bean
applicationContext.getBeanFactory().registerSingleton("initializerBean",
new InitializerBean("initialized-at-startup"));
}
}
// 自定义 BeanPostProcessor
class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof InitializerBean) {
System.out.println("初始化前处理: " + beanName);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof InitializerBean) {
System.out.println("初始化后处理: " + beanName);
}
return bean;
}
}
class InitializerBean {
private final String message;
public InitializerBean(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
// 注册初始化器(方式1:spring.factories)
/*
org.springframework.context.ApplicationContextInitializer=\
com.example.MyApplicationContextInitializer
*/
// 注册初始化器(方式2:代码注册)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.addInitializers(new MyApplicationContextInitializer());
app.run(args);
}
}
3.5 启动性能分析
import org.springframework.boot.*;
import org.springframework.boot.context.event.*;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class StartupPerformanceTracker {
private final Map<String, Long> timestamps = new ConcurrentHashMap<>();
private long startTime;
public void recordStart() {
startTime = System.currentTimeMillis();
timestamps.put("start", startTime);
}
public void recordEvent(String eventName) {
timestamps.put(eventName, System.currentTimeMillis());
}
public void printReport() {
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
System.out.println("\n========== 启动性能报告 ==========");
System.out.println("总启动时间: " + totalTime + " ms");
System.out.println("\n各阶段耗时:");
Long lastTime = startTime;
for (Map.Entry<String, Long> entry : timestamps.entrySet()) {
if (!entry.getKey().equals("start")) {
long duration = entry.getValue() - lastTime;
System.out.println(" " + entry.getKey() + ": " + duration + " ms");
lastTime = entry.getValue();
}
}
System.out.println("==================================\n");
}
}
@Component
public class PerformanceListener implements ApplicationListener<ApplicationEvent> {
private final StartupPerformanceTracker tracker;
public PerformanceListener(StartupPerformanceTracker tracker) {
this.tracker = tracker;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
String eventName = event.getClass().getSimpleName();
if (event instanceof ApplicationStartingEvent) {
tracker.recordStart();
} else {
tracker.recordEvent(eventName.replace("Event", ""));
}
if (event instanceof ApplicationReadyEvent) {
tracker.printReport();
}
}
}
// Bean 加载时间分析
@Component
public class BeanLoadTimeAnalyzer implements BeanPostProcessor {
private final Map<String, Long> loadTimes = new ConcurrentHashMap<>();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
loadTimes.put(beanName, System.currentTimeMillis());
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
Long startTime = loadTimes.get(beanName);
if (startTime != null) {
long loadTime = System.currentTimeMillis() - startTime;
if (loadTime > 100) { // 记录超过100ms的Bean
System.out.println("[慢加载Bean] " + beanName + ": " + loadTime + " ms");
}
}
return bean;
}
}
3.6 Runner 执行
import org.springframework.boot.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
// ApplicationRunner(推荐,参数类型安全)
@Component
@Order(1)
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("=== ApplicationRunner 执行 ===");
System.out.println("非选项参数: " + args.getNonOptionArgs());
System.out.println("选项参数:");
args.getOptionNames().forEach(name -> {
System.out.println(" " + name + " = " + args.getOptionValues(name));
});
// 执行初始化任务
doInitialization();
}
private void doInitialization() {
System.out.println("执行应用初始化任务...");
}
}
// CommandLineRunner(传统方式,参数为字符串数组)
@Component
@Order(2)
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("=== CommandLineRunner 执行 ===");
System.out.println("参数: " + Arrays.toString(args));
}
}
// 组合多个 Runner
@Component
public class CompositeRunner {
private final List<ApplicationRunner> applicationRunners;
private final List<CommandLineRunner> commandLineRunners;
public CompositeRunner(
List<ApplicationRunner> applicationRunners,
List<CommandLineRunner> commandLineRunners) {
this.applicationRunners = applicationRunners;
this.commandLineRunners = commandLineRunners;
}
public void executeAll(ApplicationArguments args) throws Exception {
// 按顺序执行所有 Runner
for (ApplicationRunner runner : applicationRunners) {
runner.run(args);
}
for (CommandLineRunner runner : commandLineRunners) {
runner.run(args.getSourceArgs());
}
}
}
// 条件化 Runner
@Component
@ConditionalOnProperty(name = "app.warmup.enabled", havingValue = "true")
public class WarmupRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("执行预热任务...");
// 预热缓存、建立连接等
}
}
3.7 优雅关闭
import org.springframework.context.*;
import org.springframework.stereotype.Component;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Component
public class GracefulShutdown implements DisposableBean, SmartLifecycle {
private final ExecutorService executor = Executors.newFixedThreadPool(5);
private volatile boolean running = false;
@Override
public void start() {
running = true;
System.out.println("应用启动完成");
}
@Override
public void stop() {
running = false;
System.out.println("应用停止中...");
}
@Override
public boolean isRunning() {
return running;
}
@Override
public void destroy() throws Exception {
System.out.println("执行优雅关闭...");
// 1. 停止接收新请求
stopAcceptingNewRequests();
// 2. 等待现有请求完成
executor.shutdown();
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
// 3. 释放资源
releaseResources();
System.out.println("优雅关闭完成");
}
private void stopAcceptingNewRequests() {
System.out.println("停止接收新请求");
}
private void releaseResources() {
System.out.println("释放资源");
}
}
// 关闭钩子
@Component
public class ShutdownHookConfig {
@Autowired
private ConfigurableApplicationContext context;
@PostConstruct
public void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("JVM 关闭钩子触发");
// 发布关闭事件
context.publishEvent(new ApplicationShutdownEvent(this));
// 关闭上下文
context.close();
}));
}
}
public class ApplicationShutdownEvent extends ApplicationEvent {
public ApplicationShutdownEvent(Object source) {
super(source);
}
}
// 关闭事件监听
@Component
public class ShutdownEventListener {
@EventListener
public void onShutdown(ApplicationShutdownEvent event) {
System.out.println("收到关闭事件,执行清理...");
}
@PreDestroy
public void destroy() {
System.out.println("@PreDestroy 执行清理");
}
}
四、启动流程时序图
main()
│
▼
SpringApplication.<init>()
├── 推断应用类型
├── 加载 Initializers
├── 加载 Listeners
└── 推断主类
│
▼
SpringApplication.run()
│
├── 1. 创建 StopWatch
│
├── 2. 配置 Headless 模式
│
├── 3. 获取 RunListeners
│ └── listeners.starting()
│
├── 4. 准备 Environment
│ └── listeners.environmentPrepared()
│
├── 5. 打印 Banner
│
├── 6. 创建 ApplicationContext
│
├── 7. 准备 Context
│ ├── 设置 Environment
│ ├── 执行 Initializers
│ ├── 发布 ContextPrepared
│ ├── 加载 Bean 定义
│ └── 发布 ContextLoaded
│
├── 8. 刷新 Context
│ ├── BeanFactoryPostProcessor
│ ├── 注册 BeanPostProcessor
│ ├── 实例化单例 Bean
│ └── 完成 Bean 初始化
│
├── 9. 刷新后处理
│
├── 10. 发布 Started 事件
│
├── 11. 执行 Runners
│ ├── ApplicationRunner
│ └── CommandLineRunner
│
├── 12. 发布 Ready 事件
│
└── 返回 ApplicationContext
五、总结与最佳实践
启动优化
- 延迟初始化
spring:
main:
lazy-initialization: true
- 排除不必要的自动配置
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class
})
- 使用索引
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
最佳实践
-
事件监听
- 使用 @EventListener 替代实现接口
- 避免在监听器中执行耗时操作
-
Runner 执行
- 使用 @Order 控制执行顺序
- 异常会导致应用启动失败
-
资源释放
- 实现 DisposableBean 或使用 @PreDestroy
- 注册关闭钩子
Spring Boot 启动流程是理解框架工作原理的关键。掌握启动的各个阶段和扩展点,能够帮助开发者更好地排查问题、优化性能、定制启动行为。