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

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

  • 无侵入,随时打开关闭
  • 统计结果可排序分析
  • 适合持续优化启动性能
相关推荐
程序员阿明2 小时前
spring boot在普通方法中获取HttpServletRequest及其使用的方式
java·spring boot·后端
小江的记录本2 小时前
【Docker】《 Docker 高频常用命令速查表 》
java·前端·后端·http·docker·容器·eureka
kaixiang3002 小时前
若依RuoYi实战
java·服务器·前端
SunnyDays10112 小时前
使用 Java 高效管理 Excel 分页符:添加、删除与预览全攻略
java·excel分页符
一 乐2 小时前
智能农田管理|基于springboot + vue智能农田管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·智能农田管理系统
Mem0rin2 小时前
[Java/数据结构]线性表之栈与队列
java·开发语言·数据结构
上天_去_做颗惺星 EVE_BLUE2 小时前
Go 语言入门实战指南
开发语言·后端·golang
东离与糖宝2 小时前
告别Python!Java本地部署Gemma 4:Maven一键集成
java·人工智能
吃不胖爹2 小时前
idea低版本用高版本的jdk
java·ide·intellij-idea