一、SpringBoot启动流程全景图

在深入源码之前,我们先通过一张全景图了解SpringBoot启动的整体流程:

这个流程看似简单,但每个步骤都包含了丰富的内部逻辑和扩展点。接下来我们逐一深入分析。

二、SpringApplication对象构造过程

1. 推测Web应用类型

SpringBoot在启动时会自动推测应用类型,这是整个启动过程的基础决策:

// SpringApplication构造函数中的类型推测逻辑
private WebApplicationType deduceWebApplicationType() {
    if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", null) 
        && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", null)) {
        return WebApplicationType.REACTIVE;  // 响应式Web应用
    }
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;  // 非Web应用
        }
    }
    return WebApplicationType.SERVLET;  // Servlet Web应用
}

三种应用类型说明

  • SERVLET:传统Servlet Web应用(Spring MVC)

  • REACTIVE:响应式Web应用(WebFlux)

  • NONE:非Web应用(如定时任务、批处理等)

2. 加载关键组件

SpringBoot通过SPI机制从META-INF/spring.factories加载扩展组件:

// 从spring.factories加载关键组件
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

加载的核心组件类型

组件类型 作用 典型实现
ApplicationContextInitializer Spring容器初始化器 ConditionEvaluationReportLoggingListener
ApplicationListener 事件监听器 ConfigFileApplicationListener
SpringApplicationRunListener 启动过程监听器 EventPublishingRunListener

三、run()方法执行流程详解

步骤1:启动前的准备工作

public ConfigurableApplicationContext run(String... args) {
    // 1. 创建启动时间监听器
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    // 2. 创建引导上下文
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    
    // 3. 准备环境
    ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    
    // 4. 打印Banner
    Banner printedBanner = printBanner(environment);
    
    // 5. 创建应用上下文
    context = createApplicationContext();
    
    // 6. 刷新上下文(核心)
    refreshContext(context);
    
    // 7. 启动后回调
    afterRefresh(context, applicationArguments);
    
    stopWatch.stop();
    return context;
}

步骤2:环境准备阶段

Environment对象创建与配置加载

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    
    // 创建Environment对象
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    
    // 配置Environment(加载配置文件)
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    
    // 发布Environment准备完成事件
    listeners.environmentPrepared(bootstrapContext, environment);
    
    // 绑定Environment到Spring
    bindToSpringApplication(environment);
    
    return environment;
}

配置文件加载顺序(从低到高):

  1. 默认属性(通过SpringApplication.setDefaultProperties()设置)

  2. @PropertySource注解

  3. 配置文件(application.properties/application.yml

  4. 随机值属性源(random.*

  5. 操作系统环境变量

  6. Java系统属性(System.getProperties()

  7. JNDI属性

  8. Servlet上下文参数

  9. Servlet配置参数

  10. SPRING_APPLICATION_JSON属性

  11. 命令行参数(优先级最高)

步骤3:应用上下文创建

根据应用类型创建对应的ApplicationContext:

protected ConfigurableApplicationContext createApplicationContext() {
    return this.applicationContextFactory.create(this.webApplicationType);
}

// ApplicationContextFactory.DEFAULT实现
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
    try {
        switch (webApplicationType) {
            case SERVLET:
                return new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE:
                return new AnnotationConfigReactiveWebServerApplicationContext();
            default:
                return new AnnotationConfigApplicationContext();
        }
    } catch (Exception ex) {
        throw new IllegalStateException("Unable to create ApplicationContext", ex);
    }
};

三种容器对比

容器类型 适用场景 特点
AnnotationConfigServletWebServerApplicationContext Servlet Web应用 内嵌Web服务器,支持Servlet API
AnnotationConfigReactiveWebServerApplicationContext 响应式Web应用 支持响应式编程,无阻塞IO
AnnotationConfigApplicationContext 非Web应用 轻量级,仅Spring容器功能

步骤4:容器刷新 - 核心中的核心

refresh()方法是Spring容器初始化的核心,它触发了Bean的创建、依赖注入、AOP代理等一系列关键操作:

// AbstractApplicationContext.refresh()方法
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 准备刷新
        prepareRefresh();
        
        // 2. 获取BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        
        // 3. 准备BeanFactory
        prepareBeanFactory(beanFactory);
        
        try {
            // 4. BeanFactory后置处理
            postProcessBeanFactory(beanFactory);
            
            // 5. 调用BeanFactoryPostProcessor
            invokeBeanFactoryPostProcessors(beanFactory);
            
            // 6. 注册BeanPostProcessor
            registerBeanPostProcessors(beanFactory);
            
            // 7. 初始化消息源
            initMessageSource();
            
            // 8. 初始化事件广播器
            initApplicationEventMulticaster();
            
            // 9. 初始化特殊Bean
            onRefresh();
            
            // 10. 注册监听器
            registerListeners();
            
            // 11. 实例化所有单例Bean
            finishBeanFactoryInitialization(beanFactory);
            
            // 12. 完成刷新
            finishRefresh();
        } catch (BeansException ex) {
            // 异常处理
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        } finally {
            resetCommonCaches();
        }
    }
}

步骤5:启动后扩展点执行

SpringBoot提供了两个重要的启动后扩展接口:

// ApplicationRunner接口
public interface ApplicationRunner {
    void run(ApplicationArguments args) throws Exception;
}

// CommandLineRunner接口  
public interface CommandLineRunner {
    void run(String... args) throws Exception;
}

执行顺序规则

  1. 先执行所有ApplicationRunner

  2. 再执行所有CommandLineRunner

  3. 相同接口的实现类可以通过@Order注解或实现Ordered接口指定顺序

四、关键扩展点解析

1. SpringApplicationRunListener生命周期

启动过程中通过事件机制提供了多个扩展点:

public interface SpringApplicationRunListener {
    // 启动开始
    default void starting(ConfigurableBootstrapContext bootstrapContext) {}
    
    // 环境准备完成
    default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, 
                                     ConfigurableEnvironment environment) {}
    
    // 上下文准备完成
    default void contextPrepared(ConfigurableApplicationContext context) {}
    
    // 上下文加载完成
    default void contextLoaded(ConfigurableApplicationContext context) {}
    
    // 上下文启动完成
    default void started(ConfigurableApplicationContext context) {}
    
    // 应用就绪
    default void ready(ConfigurableApplicationContext context) {}
    
    // 启动失败
    default void failed(ConfigurableApplicationContext context, Throwable exception) {}
}

默认实现EventPublishingRunListener的事件发布顺序

事件类型 触发时机 典型用途
ApplicationStartingEvent 启动开始时 日志初始化
ApplicationEnvironmentPreparedEvent 环境准备完成时 配置文件加载
ApplicationContextInitializedEvent 上下文初始化时 添加自定义Bean
ApplicationPreparedEvent 上下文准备完成时 数据源初始化
ApplicationStartedEvent 上下文启动完成时 健康检查
ApplicationReadyEvent 应用就绪时 服务注册
ApplicationFailedEvent 启动失败时 错误处理

2. ApplicationContextInitializer应用

这个接口允许我们在Spring容器刷新之前进行自定义初始化:

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C applicationContext);
}

典型应用场景

  • 注册自定义的BeanDefinition

  • 激活特定的Profile

  • 设置环境变量

  • 添加ApplicationListener

示例:自定义Initializer

public class CustomContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // 设置激活的Profile
        applicationContext.getEnvironment().addActiveProfile("dev");
        
        // 注册自定义Bean
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) applicationContext;
        RootBeanDefinition beanDefinition = new RootBeanDefinition(CustomBean.class);
        registry.registerBeanDefinition("customBean", beanDefinition);
    }
}

3. 条件注解评估报告

SpringBoot的自动配置基于条件注解,启动时会生成详细的评估报告:

// ConditionEvaluationReportLoggingListener
public class ConditionEvaluationReportLoggingListener 
        implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // 创建条件评估报告
        ConditionEvaluationReport report = ConditionEvaluationReport.get(applicationContext.getBeanFactory());
        
        // 注册报告监听器
        applicationContext.addApplicationListener(new ConditionEvaluationReportListener(report));
    }
}

查看条件评估报告

# 启动时添加debug参数
java -jar app.jar --debug

# 或者在application.properties中配置
debug=true

五、配置文件解析机制

1. 配置文件加载流程

SpringBoot通过ConfigFileApplicationListener处理配置文件加载:

2. 配置文件优先级详解

从高到低优先级顺序

// 实际加载顺序(从高优先级到低优先级)
1. 命令行参数 (--server.port=8080)
2. Java系统属性 (System.getProperties())
3. 操作系统环境变量
4. 项目根目录下的配置文件
5. 项目classpath下的配置文件
6. @Configuration注解类上的@PropertySource
7. SpringApplication.setDefaultProperties()设置的默认属性

特殊位置配置文件

  • classpath:/(项目资源根目录)

  • classpath:/config/(项目config目录)

  • file:./(当前目录)

  • file:./config/(当前config目录)

  • file:./config/*/(当前config的子目录)

3. 多环境配置支持

SpringBoot支持通过Profile实现多环境配置:

yaml

# application.yml
spring:
  profiles:
    active: dev  # 激活dev环境

---
# 开发环境配置
spring:
  config:
    activate:
      on-profile: dev
server:
  port: 8080

---
# 生产环境配置  
spring:
  config:
    activate:
      on-profile: prod
server:
  port: 80

六、内嵌Web服务器启动

1. Tomcat自动配置原理

对于Servlet Web应用,SpringBoot会自动配置内嵌Tomcat:

// ServletWebServerFactoryAutoConfiguration
@AutoConfiguration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public class ServletWebServerFactoryAutoConfiguration {
    
    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }
}

2. Web服务器启动流程

// SpringApplication.refresh()中的onRefresh()方法
protected void onRefresh() {
    super.onRefresh();
    try {
        createWebServer();  // 创建Web服务器
    } catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    
    if (webServer == null && servletContext == null) {
        // 获取WebServerFactory
        ServletWebServerFactory factory = getWebServerFactory();
        
        // 创建WebServer
        this.webServer = factory.getWebServer(getSelfInitializer());
    } else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        } catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}

七、启动优化与问题排查

1. 启动性能优化建议

// 1. 延迟初始化配置
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.setLazyInitialization(true);  // 启用延迟初始化
        app.run(args);
    }
}

// 2. 排除不必要的自动配置
@SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class,
    KafkaAutoConfiguration.class
})
public class Application {
    // ...
}

2. 常见启动问题排查

问题1:Bean创建失败

bash

# 查看详细的Bean创建日志
logging.level.org.springframework.beans=DEBUG

问题2:自动配置不生效

bash

# 启用条件评估报告
debug=true
# 或通过JMX查看
spring.application.admin.enabled=true

问题3:端口被占用

// 自定义端口检测逻辑
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> portCustomizer() {
    return factory -> {
        int port = SocketUtils.findAvailableTcpPort(8080, 9000);
        factory.setPort(port);
    };
}

3. 自定义启动脚本示例

@SpringBootApplication
public class Application {
    
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        
        // 自定义启动配置
        app.setBannerMode(Banner.Mode.CONSOLE);
        app.setLogStartupInfo(true);
        app.setAddCommandLineProperties(true);
        
        // 添加自定义监听器
        app.addListeners(new ApplicationStartingListener());
        
        // 运行应用
        ConfigurableApplicationContext context = app.run(args);
        
        // 获取启动信息
        StartupInfoLogger logger = new StartupInfoLogger(Application.class);
        logger.logStarted(getApplicationLog(), context);
    }
}

八、总结与最佳实践

核心要点回顾

  1. 启动流程:构造SpringApplication → 推测应用类型 → 加载扩展组件 → 准备环境 → 创建容器 → 刷新容器 → 执行Runner

  2. 扩展点:合理使用ApplicationContextInitializer、ApplicationListener、SpringApplicationRunListener进行定制

  3. 配置优先级:命令行参数 > 系统属性 > 环境变量 > 配置文件

  4. 容器选择:根据应用类型自动选择Servlet/Reactive/普通容器

最佳实践建议

  1. Profile管理:使用Profile分离不同环境配置

  2. 延迟初始化:大型应用启用延迟初始化提升启动速度

  3. 条件注解:善用@Conditional系列注解控制Bean加载

  4. 启动监控:通过Actuator监控启动性能和Bean加载情况

  5. 自定义Banner:通过banner.txt定制启动Logo

调试技巧

bash

# 1. 查看详细的启动日志
java -jar app.jar --debug

# 2. 查看Bean定义
java -jar app.jar --spring.application.admin.enabled=true

# 3. 性能分析
java -jar app.jar -Dspring.profiles.active=timing

深入理解SpringBoot启动过程,不仅能帮助我们更好地使用框架,还能在遇到问题时快速定位和解决。掌握这些核心机制,你将能更加游刃有余地进行SpringBoot应用开发和调优。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐