SpringBoot应用启动机制的使用详解
作者:汪不止
这篇文章主要介绍了SpringBoot应用启动机制的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
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 = 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 初始化流程
// 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 创建生命周期
// 单个 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 条件化配置加载
// 自动配置原理 @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 服务器启动
// 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 本地启动 | 服务器部署启动 |
---|---|---|
类加载机制 | 标准 ClassLoader | LaunchedURLClassLoader |
配置文件加载 | 文件系统直接读取 | 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 - 自动重启 - 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
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。