不是。默认情况下spring预实例化所有单例bean,导致启动耗时;@lazy可延迟非核心bean初始化,但需避开基础设施bean;spring-context-indexer跳过扫描提升启动速度,而静态块、自动配置条件、容器初始化等常被忽略的阻塞点才是真因。

Spring Boot 启动慢,是不是所有 Bean 都在启动时创建?
不是。默认情况下,Spring 容器会预实例化所有单例 @Component、@Service 等标注的 Bean,哪怕你只在某个 API 调用时才用到它。这是启动耗时的主要来源之一。
关键判断:如果你的应用有大量非核心组件(比如报表导出工具、定时任务辅助类、第三方 SDK 封装),它们很可能在启动阶段就被初始化了,但实际运行中根本没被调用过。
-
@Lazy可以让 Bean 推迟到第一次被注入或ApplicationContext.getBean()时才创建 - 加在类上:整个 Bean 延迟初始化;加在
@Autowired字段上:仅该依赖延迟(注意:字段必须是非 final 的,否则运行时报BeanCreationException: @Lazy on a final field) - 不建议全局加
@Lazy,尤其不要加在@Configuration类或数据源、事务管理器等基础设施 Bean 上,否则可能引发NullPointerException或代理失效
哪些 Bean 适合加 @Lazy?看使用场景和依赖链
适合加 @Lazy 的典型场景是:启动即加载但运行期极少调用、初始化开销大、依赖外部服务(如 Redis 连接池、Elasticsearch 客户端)、或仅被某个特定 Controller/Job 触发。
- 例如:一个封装了复杂 PDF 渲染逻辑的
PdfGeneratorService,只在 /export/pdf 接口里用到 → 加@Lazy安全且有效 - 再如:一个监听 Kafka 主题的
@KafkaListener所在的 Service,只要它没被其他 Bean 直接注入,加@Lazy不影响消费(因为 Kafka listener 是由 Spring Kafka 自行触发的) - 反例:一个被
@Transactional方法调用的OrderService,如果它又依赖了加了@Lazy的InventoryClient,而InventoryClient初始化要连远程 HTTP 服务 —— 那首次下单就会卡住几秒,用户感知明显
spring-context-indexer 能省多少时间?它到底在做什么?
它不减少 Bean 创建数量,而是跳过 classpath 扫描,把 @Component、@Configuration 等元数据提前编译成 META-INF/spring.components 文件。对模块多、jar 包多的项目,能省掉几十到几百毫秒扫描时间。
- 只对 Maven/Gradle 构建生效,需显式引入处理器:
org.springframework:spring-context-indexer(scope=provided) - 生成的
spring.components文件体积很小,但要求所有模块都启用 indexer,否则混合模式下 Spring 会 fallback 到全量扫描 - 常见坑:
spring-boot-devtools在热重启时可能忽略新生成的 index 文件,建议开发期关掉 indexer(通过spring.context.index.enabled=false),上线再打开 - 验证是否生效:启动日志里搜
Using generated Spring configuration metadata,出现即表示 index 已加载
为什么加了 @Lazy 还是启动慢?检查这三点
延迟加载不是银弹。很多“启动慢”问题其实和 Bean 初始化无关,而是被掩盖的阻塞点。
- 静态代码块或
static {}初始化耗时(比如加载大字典、解析本地 JSON 配置)→ 这部分不受@Lazy控制,得改造成懒加载逻辑 - 自动配置类(
@ConditionalOnClass等)触发的类加载,尤其是某些 SDK 强依赖反射或资源读取 → 可通过debug=true启动参数看ConditionEvaluationReport,定位哪个 autoconfig 卡住了 - 嵌入式容器(Tomcat/Jetty)自身初始化,比如 SSL 证书加载、JNDI 查找、HTTP/2 配置 → 和 Spring Bean 无关,得查 server 相关日志或用
-Djavax.net.debug=ssl排查
真正影响启动耗时的,往往是那些你以为“只是个注解”的地方:一个没留意的 @PostConstruct 方法、一段没 mock 掉的测试数据预热逻辑、甚至某个 starter 的 ApplicationRunner 里写了同步 HTTP 调用。这些比要不要加 @Lazy 更值得先揪出来。










