Spring Boot 应用启动机制详解

Spring Boot 应用启动机制详解

IDEA 中启动 Spring Boot 的详细过程

1. 预启动阶段

1.1 环境检测与验证
java 复制代码
// IDEA 执行的前置检查
- JDK 版本兼容性验证
- 项目依赖完整性检查
- Spring Boot 版本与插件匹配
- 构建工具配置验证(Maven/Gradle)
- 应用配置文件语法检查
1.2 类路径构建
text 复制代码
类路径组成:
├── 项目编译输出目录 (target/classes 或 build/classes)
├── 依赖库 (Maven: ~/.m2/repository, Gradle: ~/.gradle/caches)
├── 资源文件 (src/main/resources)
├── 测试资源文件 (src/test/resources) [测试时]
└── IDEA 特定模块路径

2. Spring Boot 应用启动核心流程

2.1 SpringApplication 初始化阶段
java 复制代码
public class SpringApplication {
    
    public SpringApplication(Class<?>... primarySources) {
        // 1. 主配置类存储
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        
        // 2. 推断应用类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        
        // 3. 加载 ApplicationContextInitializer
        setInitializers(getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
        
        // 4. 加载 ApplicationListener
        setListeners(getSpringFactoriesInstances(ApplicationListener.class));
        
        // 5. 推断主应用类
        this.mainApplicationClass = deduceMainApplicationClass();
    }
}
2.2 运行阶段详细分解
java 复制代码
public ConfigurableApplicationContext run(String... args) {
    // 阶段 1: 启动准备
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    // 阶段 2: 监听器通知 - ApplicationStartingEvent
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    
    try {
        // 阶段 3: 环境准备
        ApplicationArguments applicationArguments = 
            new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        
        // 阶段 4: 配置忽略的 Bean 信息
        configureIgnoreBeanInfo(environment);
        
        // 阶段 5: 打印 Banner
        Banner printedBanner = printBanner(environment);
        
        // 阶段 6: 创建应用上下文
        context = createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        
        // 阶段 7: 准备上下文
        prepareContext(context, environment, listeners, 
                      applicationArguments, printedBanner);
        
        // 阶段 8: 刷新上下文(核心)
        refreshContext(context);
        
        // 阶段 9: 刷新后处理
        afterRefresh(context, applicationArguments);
        
        // 阶段 10: 启动完成通知
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);
        }
        
        // 阶段 11: 发布 ApplicationReadyEvent
        listeners.started(context);
        
        // 阶段 12: 执行 Runner Bean
        callRunners(context, applicationArguments);
        
        // 阶段 13: 发布 ApplicationStartedEvent
        listeners.ready(context, stopWatch);
        
    } catch (Throwable ex) {
        handleRunFailure(context, listeners, ex);
        throw new IllegalStateException(ex);
    }
    
    return context;
}

3. 上下文刷新详细过程

3.1 BeanFactory 初始化流程
java 复制代码
// AbstractApplicationContext.refresh() 完整流程
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
        
        // 步骤 1: 准备刷新 - 设置启动日期、激活状态等
        prepareRefresh();
        
        // 步骤 2: 获取新的 BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        
        // 步骤 3: 准备 BeanFactory 使用
        prepareBeanFactory(beanFactory);
        
        try {
            // 步骤 4: 允许 BeanFactory 后处理
            postProcessBeanFactory(beanFactory);
            
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            
            // 步骤 5: 调用 BeanFactoryPostProcessor
            invokeBeanFactoryPostProcessors(beanFactory);
            
            // 步骤 6: 注册 BeanPostProcessor
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();
            
            // 步骤 7: 初始化消息源
            initMessageSource();
            
            // 步骤 8: 初始化事件广播器
            initApplicationEventMulticaster();
            
            // 步骤 9: 初始化特殊 Bean(模板方法)
            onRefresh();
            
            // 步骤 10: 注册监听器
            registerListeners();
            
            // 步骤 11: 完成 BeanFactory 初始化
            finishBeanFactoryInitialization(beanFactory);
            
            // 步骤 12: 完成刷新
            finishRefresh();
        } catch (BeansException ex) {
            // 异常处理...
        } finally {
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}
3.2 Bean 创建生命周期
java 复制代码
// 单个 Bean 的完整创建过程
1. 实例化 Bean (构造函数调用)
2. 属性注入 (@Autowired, @Value, @Resource)
3. Aware 接口回调 (BeanNameAware, BeanFactoryAware, ApplicationContextAware)
4. BeanPostProcessor.postProcessBeforeInitialization()
5. @PostConstruct 方法执行
6. InitializingBean.afterPropertiesSet() 执行
7. 自定义初始化方法 (init-method)
8. BeanPostProcessor.postProcessAfterInitialization()
9. Bean 就绪,加入单例池
10. 应用场景:
    - 单例 Bean: 启动时创建
    - 原型 Bean: 每次获取时创建
    - 延迟加载: 第一次使用时创建

4. Spring Boot 自动配置机制

4.1 条件化配置加载
java 复制代码
// 自动配置原理
@SpringBootApplication
├── @SpringBootConfiguration
├── @EnableAutoConfiguration
│   └── @Import(AutoConfigurationImportSelector.class)
└── @ComponentScan

// AutoConfigurationImportSelector 工作流程:
1. 加载 META-INF/spring.factories 中所有 EnableAutoConfiguration 配置
2. 根据条件注解过滤:
   - @ConditionalOnClass
   - @ConditionalOnBean
   - @ConditionalOnProperty
   - @ConditionalOnWebApplication
   - @ConditionalOnMissingBean
3. 按 @AutoConfigureOrder、@Order 排序
4. 去重并应用配置类
4.2 内嵌 Web 服务器启动
java 复制代码
// Tomcat 启动详细过程
1. ServletWebServerApplicationContext.onRefresh()
2. createWebServer() 创建 WebServer
3. TomcatServletWebServerFactory.getWebServer()
   - 创建 Tomcat 实例
   - 配置 Engine 和 Host
   - 创建 Connector(配置端口、协议等)
   - 创建 Context 并配置
   - 加载 DispatcherServlet
   - 配置 Session、ErrorPage 等
4. 启动 Tomcat
   - 启动 Connector 监听端口
   - 启动 Engine 处理请求
5. 发布 ServletWebServerInitializedEvent

服务器部署启动的详细过程

1. 打包与部署准备

1.1 可执行 JAR 结构
text 复制代码
my-application.jar
├── META-INF/
│   └── MANIFEST.MF
├── BOOT-INF/
│   ├── classes/          # 应用类文件
│   │   ├── com/yourcompany/Application.class
│   │   └── application.properties
│   └── lib/              # 依赖库
│       ├── spring-boot-2.7.x.jar
│       ├── spring-core-5.3.x.jar
│       └── ...
└── org/springframework/boot/loader/
    ├── JarLauncher.class
    └── LaunchedURLClassLoader.class
1.2 启动脚本示例
bash 复制代码
#!/bin/bash
# 生产环境启动脚本

# JVM 参数配置
JAVA_OPTS="-server -Xms2g -Xmx2g -XX:+UseG1GC"
JAVA_OPTS="$JAVA_OPTS -XX:MaxGCPauseMillis=200"
JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=prod"
JAVA_OPTS="$JAVA_OPTS -Dlogging.file=/var/log/myapp/application.log"

# 启动应用
java $JAVA_OPTS -jar my-application.jar

2. 生产环境启动流程

2.1 启动类加载器机制
java 复制代码
// Spring Boot Launcher 工作机制
JarLauncher -> LaunchedURLClassLoader
    ↓
加载 BOOT-INF/classes 和 BOOT-INF/lib/*.jar
    ↓
反射调用应用的 main 方法
    ↓
后续流程与 IDEA 启动相同
2.2 生产环境特定配置
yaml 复制代码
# application-prod.yml
spring:
  datasource:
    url: jdbc:mysql://prod-db:3306/myapp
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  redis:
    host: redis-cluster
    port: 6379
    
server:
  port: 8080
  compression:
    enabled: true
  servlet:
    session:
      timeout: 30m

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics
  endpoint:
    health:
      show-details: always

本地启动与服务器启动的异同点对比

1. 环境配置差异

特性 IDEA 本地启动 服务器部署启动
类加载机制 标准 ClassLoader LaunchedURLClassLoader
配置文件加载 文件系统直接读取 JAR 包内资源读取
热部署支持 DevTools 自动重启 需要手动重启
调试支持 完整调试功能 远程调试需配置
资源监控 IDEA 内置工具 JMX/Actuator 监控

2. 性能特征对比

2.1 启动时间分析
java 复制代码
// 本地开发环境(IDEA)
启动阶段                 | 时间占比
------------------------------------
类路径扫描和加载        | 15-20%
Bean 定义解析          | 20-25%
Bean 实例化和依赖注入  | 30-35%
Web 服务器启动         | 15-20%
其他初始化            | 10-15%

// 生产服务器环境
启动阶段                 | 时间占比
------------------------------------
JAR 解压和类加载       | 25-30%
Bean 定义解析          | 20-25%
Bean 实例化和依赖注入  | 25-30%
Web 服务器启动         | 15-20%
其他初始化            | 5-10%
2.2 内存使用对比
java 复制代码
// 开发环境典型内存配置
-Xms512m -Xmx1024m -XX:MaxMetaspaceSize=256m

// 生产环境典型内存配置  
-Xms2g -Xmx2g -XX:MaxMetaspaceSize=512m
-XX:+UseG1GC -XX:MaxGCPauseMillis=200

3. 配置管理差异

3.1 配置文件加载策略
properties 复制代码
# 开发环境配置优先级
1. @TestPropertySource
2. 命令行参数 (IDEA Run Configuration)
3. SPRING_APPLICATION_JSON
4. ServletConfig 初始化参数
5. ServletContext 初始化参数  
6. JNDI 属性
7. Java 系统属性
8. 操作系统环境变量
9. random.* 属性
10. application-{profile}.properties/yml
11. application.properties/yml
12. @PropertySource
13. 默认属性

# 生产环境配置优先级
1. 命令行参数 (启动脚本)
2. SPRING_APPLICATION_JSON
3. Java 系统属性  
4. 操作系统环境变量
5. random.* 属性
6. application-{profile}.properties/yml (打包在JAR内)
7. application.properties/yml (打包在JAR内)
8. 默认属性
3.2 日志配置差异
yaml 复制代码
# 开发环境日志配置
logging:
  level:
    com.yourcompany: DEBUG
    org.springframework: INFO
  pattern:
    console: "%clr(%d{HH:mm:ss.SSS}){faint} %clr(%-5level) %clr(${PID}){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx"

# 生产环境日志配置  
logging:
  file:
    path: /var/log/myapp
    name: /var/log/myapp/application.log
  level:
    com.yourcompany: INFO
    org.springframework: WARN
  pattern:
    file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

4. 监控和管理差异

4.1 开发环境监控
java 复制代码
// IDEA 内置工具
- 内存使用情况实时监控
- CPU 使用率分析
- 线程状态查看
- 断点调试和变量查看
- 方法执行时间分析

// Spring Boot DevTools
- 自动重启
- LiveReload
- 全局配置
- 远程调试支持
4.2 生产环境监控
yaml 复制代码
# Spring Boot Actuator 配置
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,env,beans
      base-path: /manage
  endpoint:
    health:
      show-details: always
      probes:
        enabled: true
    metrics:
      enabled: true
  metrics:
    export:
      prometheus:
        enabled: true

# 健康检查配置
spring:
  boot:
    admin:
      client:
        url: http://monitoring-server:8080

性能优化建议

1. 启动性能优化

1.1 类路径优化
java 复制代码
// 减少不必要的依赖
@SpringBootApplication
// 排除不必要的自动配置
@EnableAutoConfiguration(exclude = {
    DataSourceAutoConfiguration.class,
    DataSourceTransactionManagerAuto.class
})
public class Application {
    // 延迟初始化配置
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.setLazyInitialization(true);  // 延迟初始化
        app.run(args);
    }
}
1.2 Bean 初始化优化
java 复制代码
@Component
public class HeavyBean {
    
    @PostConstruct
    public void init() {
        // 异步初始化耗时操作
        CompletableFuture.runAsync(() -> {
            // 耗时初始化逻辑
            heavyInitialization();
        });
    }
    
    // 使用 @Lazy 延迟加载
    @Bean
    @Lazy
    public ExpensiveService expensiveService() {
        return new ExpensiveService();
    }
}

2. 内存使用优化

2.1 JVM 参数调优
bash 复制代码
# 生产环境推荐配置
java -server -Xms2g -Xmx2g \
     -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
     -XX:InitiatingHeapOccupancyPercent=45 \
     -XX:+ExplicitGCInvokesConcurrent \
     -Xlog:gc*:file=/var/log/myapp/gc.log:time,uptime,level,tags:filecount=5,filesize=10m \
     -jar my-application.jar

常见问题与解决方案

1. 启动失败问题

1.1 类冲突问题
java 复制代码
// 解决方案:排除冲突依赖
<dependency>
    <groupId>com.some.library</groupId>
    <artifactId>problematic-lib</artifactId>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

// 或者使用 dependencyManagement 统一版本
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-bom</artifactId>
            <version>2.13.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
1.2 配置加载问题
properties 复制代码
# 配置加载顺序问题解决方案
# 1. 使用明确的配置文件
spring.config.location=classpath:/,classpath:/config/,file:./,file:./config/

# 2. 环境变量覆盖
export SPRING_APPLICATION_JSON='{"server":{"port":8080}}'

# 3. 配置属性验证
@Component
@ConfigurationProperties(prefix = "app.datasource")
@Validated
public class DataSourceProperties {
    @NotEmpty
    private String url;
    
    // getters and setters
}

2. 性能问题诊断

2.1 启动时间分析
java 复制代码
// 启用启动时间监控
@SpringBootApplication
public class Application {
    
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        
        // 添加启动监听器记录时间
        app.addListeners(new ApplicationListener<ApplicationReadyEvent>() {
            @Override
            public void onApplicationEvent(ApplicationReadyEvent event) {
                // 记录启动时间
                log.info("Application started in {} seconds", 
                    ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0);
            }
        });
        
        app.run(args);
    }
}

// 或者使用 Spring Boot 的启动指标
management.endpoints.web.exposure.include=startup
相关推荐
伯明翰java9 小时前
mybatis-generator插件自动生成mapper及其实体模型配置
java·开发语言·mybatis
聪明的笨猪猪9 小时前
Java Spring “Bean” 面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
一叶飘零_sweeeet9 小时前
从 0 到 1 搭建实时数据看板:RabbitMQ+WebSocket 实战指南
java·websocket·rabbitmq·数据看板
FengyunSky9 小时前
高通Camx内存问题排查
android·linux·后端
咖啡啡不加糖9 小时前
贪心算法详解与应用
java·后端·算法·贪心算法
寒月霜华9 小时前
java-网络编程-UDP,TCP通信
java·网络·tcp/ip·udp
matlab的学徒10 小时前
nginx+springboot+redis+mysql+elfk
linux·spring boot·redis·nginx
DN金猿10 小时前
java8提取list中对象有相同属性值的对象或属性值
java·list·stream·java8
IT_陈寒10 小时前
Java性能优化:3个90%开发者都忽略的高效技巧,让你的应用提速50%!
前端·人工智能·后端