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 启动慢的根因。

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

  • 无侵入,随时打开关闭
  • 统计结果可排序分析
  • 适合持续优化启动性能
相关推荐
panxianren18 小时前
Bun 1.4 的 bun test --parallel 将 4 秒的测试套件缩短到约 1 秒
java·javascript
码事漫谈20 小时前
骂AI,会让它做得更好吗?
后端
sunshine22 girl20 小时前
Java学习七 Java 项目实战2-注册业务
java·学习
萧瑟余晖20 小时前
Spring Boot 核心理念与快速上手
spring boot
陈随易20 小时前
bm2,Node.js 与 Bun 项目部署新选择
前端·后端·程序员
省钱兄--zs20 小时前
北京24小时自助健身房系统软件开发实战:从架构设计到部署指南
java·数据仓库·spring boot·小程序·需求分析
youdexiang21 小时前
Android录音软件时间关键词检索功能分析
java·人工智能
架构技术专栏21 小时前
AI Agent 框架怎么选:从一次制度查询拆出技术边界
后端·面试
sunshine22 girl21 小时前
Java学习七 Java 项目实战1-项目创建和主页面搭建
java·开发语言·学习
code2cat21 小时前
Java进阶篇之AtomicStampedReference:引用回到原值,版本戳仍能记住变化
java·cas·并发编程·aba