23-Spring Boot 启动流程详解

4 阅读7分钟

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 定义的来源:

  1. 主配置类:@SpringBootApplication 标注的类
  2. @ComponentScan:扫描的组件
  3. @Import:导入的配置类
  4. spring.factories:自动配置类
  5. @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

五、总结与最佳实践

启动优化

  1. 延迟初始化
spring:
  main:
    lazy-initialization: true
  1. 排除不必要的自动配置
@SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class
})
  1. 使用索引
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

最佳实践

  1. 事件监听

    • 使用 @EventListener 替代实现接口
    • 避免在监听器中执行耗时操作
  2. Runner 执行

    • 使用 @Order 控制执行顺序
    • 异常会导致应用启动失败
  3. 资源释放

    • 实现 DisposableBean 或使用 @PreDestroy
    • 注册关闭钩子

Spring Boot 启动流程是理解框架工作原理的关键。掌握启动的各个阶段和扩展点,能够帮助开发者更好地排查问题、优化性能、定制启动行为。