Spring Boot 启动慢排查与优化实战指南

本文总结 Spring Boot 启动耗时过长的常见原因,并给出一整套可落地的排查方案 ,包括启用 startup 端点分析、编写 Bean 初始化耗时记录器、输出 Top10 耗时 Bean 等,帮助快速定位并解决启动瓶颈问题。


📌 1. 启用 Actuator startup 端点

Spring Boot 2.4+ 引入了 ApplicationStartup 机制,可以记录启动各阶段耗时。

我们可以借助 spring-boot-starter-actuator/startup 端点来分析:

添加依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置 application.yml

这里我们将 Actuator 单独跑在 12306 端口上,路径为 /java

yaml 复制代码
management:
  server:
    port: 12306
  endpoints:
    web:
      base-path: /java
      exposure:
        include: '*'
  endpoint:
    startup:
      enabled: true

在启动类中设置 BufferingApplicationStartup

必须在 run() 之前设置

java 复制代码
@SpringBootApplication
public class ServiceApplication {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(ServiceApplication.class);
        app.setApplicationStartup(new BufferingApplicationStartup(2048));
        app.run(args);
    }
}

访问启动耗时信息

应用启动后,访问:

复制代码
http://localhost:12306/java/startup

即可查看所有 Bean 初始化和自动配置加载的耗时信息。


📌 2. 打印 Bean 初始化耗时日志(实时查看)

访问 /startup 虽然可以分析启动,但需要打开浏览器。

我们可以编写一个 BeanPostProcessor,在启动时实时输出每个 Bean 的初始化耗时

java 复制代码
@Component
public class BeanStartupTimeLogger implements SmartInstantiationAwareBeanPostProcessor {

    private final ConcurrentHashMap<String, Long> startTimes = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, Long> durations = new ConcurrentHashMap<>();

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        startTimes.put(beanName, System.nanoTime());
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Long start = startTimes.remove(beanName);
        if (start != null) {
            long durationMs = (System.nanoTime() - start) / 1_000_000;
            durations.put(beanName, durationMs);
            System.out.printf("[Bean Init] %-60s %d ms%n", beanName, durationMs);
        }
        return bean;
    }

    /** 启动完成后打印 Top 10 */
    @EventListener(ContextRefreshedEvent.class)
    public void onContextRefreshed() {
        System.out.println("\n================= Bean 初始化耗时 Top 10 =================");
        durations.entrySet().stream()
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .limit(10)
                .forEach(e -> System.out.printf("%-60s %d ms%n", e.getKey(), e.getValue()));
        System.out.println("==========================================================\n");
    }
}

效果示例:

复制代码
[Bean Init] requestMappingHandlerMapping                       956 ms
[Bean Init] jacksonObjectMapper                                758 ms
[Bean Init] jdbcTemplate                                       586 ms
...
================= Bean 初始化耗时 Top 10 =================
requestMappingHandlerMapping                       956 ms
jacksonObjectMapper                                758 ms
jdbcTemplate                                       586 ms
...
==========================================================

📌 3. 常见启动慢的根因定位

根据统计出的 Top 耗时 Bean,通常可以快速推断原因:

耗时 Bean 典型原因 优化建议
requestMappingHandlerMapping Controller 数量多,包扫描范围大 限定 @ComponentScan 范围
jacksonObjectMapper 类/模块过多,加载慢 减少 @JsonComponent/@JsonMixin 模块
jdbcTemplate / transactionManager 初始化访问数据库,连接慢 配置连接池超时,延迟初始化
redisKeyValueTemplate / reactiveRedisTemplate 引入 Redis Repository 自动配置 如果未使用 Repository,可 exclude
openApiResource Springdoc-OpenAPI 初始化大量类 只在开发环境启用 swagger

📌 4. 通用优化策略

  • 使用 spring.main.lazy-initialization=true 推迟非必要 Bean 初始化
  • 精简 @ComponentScan 扫描包范围
  • 排除不必要的 Starter / AutoConfiguration
  • 检查数据库、Redis 等外部依赖的连接耗时(DNS/网络/超时)
  • 使用 JFR / VisualVM 等分析启动 CPU 时间

📌 5. 总结

通过 /startup + BeanStartupTimeLogger,可以精准捕捉每个 Bean 初始化耗时,定位 Spring Boot 启动慢的根因。

相比盲目猜测,这种方式:

  • 无侵入,随时打开关闭
  • 统计结果可排序分析
  • 适合持续优化启动性能
相关推荐
QC班长3 小时前
Maven公司私库配置踩坑点
java·服务器·maven·intellij-idea
Makoto_Kimur3 小时前
java开发面试-AI Coding速成
java·开发语言
wuqingshun3141594 小时前
说说mybatis的缓存机制
java·缓存·mybatis
空中海4 小时前
Kubernetes 生产实践、可观测性与扩展入门
java·贪心算法·kubernetes
Devin~Y4 小时前
大厂Java面试实录:Spring Boot/Cloud、Kafka、Redis、K8s 与 Spring AI(RAG/Agent)三轮连环问
java·spring boot·redis·mysql·spring cloud·kafka·kubernetes
bLEd RING4 小时前
SpringBoot3.3.0集成Knife4j4.5.0实战
java
小松加哲5 小时前
Spring MVC 核心原理全解析
java·spring·mvc
GetcharZp5 小时前
比 Zap 还要快?Go 社区高性能日志神器 Zerolog 落地实践指南
后端
Ulyanov5 小时前
《PySide6 GUI开发指南:QML核心与实践》 第二篇:QML语法精要——构建声明式UI的基础
java·开发语言·javascript·python·ui·gui·雷达电子对抗系统仿真
码界筑梦坊5 小时前
357-基于Java的大型商场应急预案管理系统
java·开发语言·毕业设计·知识分享