开发者

SpringBoot应用启动机制的使用详解

目录
  • IDEA中启动SpringBoot的过程
    • 1. 预启动阶段
      • 1.1 环境检测与验证
      • 1.2 类路径构建
    • 2. Spring Boot 应用启动核心流程
      • 2.1 SpringApplication 初始化阶段
      • 2.2 运行阶段详细分解
    • 3. 上下文刷新详细过程
      • 3.1 BeanFactory 初始化流程
      • 3.2 Bean 创建生命周期
    • 4. Spring Boot 自动配置机制
      • 4.1 条件化配置加载
      • 4.2 内嵌 Web 服务器启动
  • 服务器部署启动的详细过程
    • 1. 打包与部署准备
      • 1.1 可执行 JAR 结构
      • 1.2 启动脚本示例
    • 2. 生产环境启动流程
      • 2.1 启动类加载器机制
      • 2.2 生产环境特定配置
  • 本地启动与服务器启动的异同点对比
    • 1. 环境配置差异
      • 2. 性能特征对比
        • 2.1 启动时间分析
        • 2.2 内存使用对比
      • 3. 配置管理差异
        • 3.1 配置文件加载策略
        • 3.2 日志配置差异
      • 4. 监控和管理差异
        • 4.1 开发环境监控
        • 4.2 生产环境监控
    • 性能优化建议
      • 1. 启动性能优化
        • 1.1 类路径优化
        • 1.2 Bean 初始化优化
      • 2. 内存使用优化
        • 2.1 JVM 参数调优
    • 常见问题与解决方案
      • 1. 启动失败问题
        • 1.1 类冲突问题
        • 1.2 配置加载问题
      • 2. 性能问题诊断
        • 2.1 启动时间分析
    • 总结

      IDEA中启动SpringBoot的过程

      1. 预启动阶段

      1.1 环境检测与验证

      // IDEA 执行的前置检查

      • - JDK 版本兼容性验证
      • - 项目依赖完整性检查
      • - Spring Boot 版本与插件匹配
      • - 构建工具配置验证(Maven/Gradle)
      • - 应用配置文件语法检查

      1.2 类路径构建

      类路径组成:

      ├── 项目编译输出目录 (target/classes 或 build/classes)

      ├── 依赖库 (Maven: ~/.m2/repository, Gradle: ~/.gradle/caches)

      ├── 资源文件 (src/main/resources)

      ├── 测试资源文件 (src/test/resources) [测试时]

      └── IDEA 特定模块路径

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

      2.1 SpringApplication 初始化阶段

      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 运行阶段详细分解

      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 = prepjavascriptareEnvironment(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 初始化流程

      // AbstractApplicationContext.refresh() 完整流程
      @Override
      public void refresh() throws BeansException, IllegalStateException {
          synchronized (this.startupShutdownMonitor) {
              StartupStejsp 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 创建生命周期

      // 单个 Bean 的完整创建过程
      1. 实例化 Bean (构造函数调用)
      2. 属性注入 (@Autowired, @ValuKdIwgne, @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 条件化配置加载

      // 自动配置原理
      @SpringBootApplication
      ├── @SpringBootConfiguration
      ├── @EnableAutoConfiguration
      │   └── @Import(AutoConfigurationImportSelector.class)
      └── @ComponentScan
      
      // AutoConfigurationImportSelector 工作流程:
      1. 加载 META-INF/spring.factories 中所有 EnableAutoConfiguration 配置
      2. 根据条件注解过滤:
         - @ConditionalOnC编程lass
         - @ConditionalOnBean
         - @ConditionalOnProperty
         - @ConditionalOnWebApplication
         - @ConditionalOnMissingBean
      3. 按 @AutoConfigureOrder、@Order 排序
      4. 去重并应用配置类
      

      4.2 内嵌 Web 服务器启动

      // 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 结构

      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 启动脚本示例

      #!/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 启动类加载器机制

      // Spring Boot Launcher 工作机制
      JarLauncher -> LaunchedURLClassLoader
          ↓
      加载 BOOT-INF/classes 和 BOOT-INF/lib/*.jar
          ↓
      反射调用应用的 main 方法
          ↓
      后续流程与 IDEA 启动相同
      

      2.2 生产环境特定配置

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

      2. 性能特征对比

      2.1 启动时间分析

      // 本地开发环境(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 内存使用对比

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

      3. 配置管理差异

      3.1 配置文件加载策略

      # 开发环境配置优先级
      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 日志配置差异

      # 开发环境日志配置
      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 开发环境监控

      // IDEA 内置工具
      - 内存使用情况实时监控
      - CPU 使用率分析
      - 线程状态查看
      - 断点调试和变量查看
      - 方法执行时间分析
      
      // Spring Boot DevTools
      - 自动重启
      -php LiveReload
      - 全局配置
      - 远程调试支持
      

      4.2 生产环境监控

      # 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 类路径优化

      // 减少不必要的依赖
      @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 初始化优化

      @Component
      public class HeavyBean {
          
          @PostConstruct
          public void init() {
              // 异步初始化耗时操作
              CompletableFuture.runAsync(() -> {
                  // 耗时初始化逻辑
                  heavyInitialization();
              });
          }
          
          // 使用 @Lazy 延迟加载
          @Bean
          @Lazy
          public ExpensiveService expensiveService() {
              return new ExpensiveService();
          }
      }
      

      2. 内存使用优化

      2.1 JVM 参数调优

      # 生产环境推荐配置
      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 类冲突问题

      // 解决方案:排除冲突依赖
      <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 配置加载问题

      # 配置加载顺序问题解决方案
      # 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 启动时间分析

      // 启用启动时间监控
      @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
      

      总结

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜