当前位置: 首页 > news >正文

Spring Boot 应用启动组件加载顺序与优先级详解

这是一个非常核心且重要的问题!理解 Spring Boot 应用启动过程中组件的加载顺序和优先级,对于开发自定义 Starter、解决 Bean 依赖冲突、调试启动失败、优化启动性能都至关重要。

下面我将为你系统、详细、结构化地讲解 Spring Boot 启动过程中的组件加载顺序与优先级机制,并配以中文注释示例和实战建议


🚀 Spring Boot 应用启动组件加载顺序与优先级详解


📌 一、整体启动流程概览(简化版)

Spring Boot 启动大致经历以下阶段:

1. SpringApplication 实例化
2. 加载监听器(ApplicationListener)
3. 环境准备(EnvironmentPreparedEvent)
4. ApplicationContext 创建
5. BeanDefinition 加载(@ComponentScan, @Import, AutoConfiguration)
6. Bean 实例化(依赖注入、初始化)
7. ApplicationRunner / CommandLineRunner 执行
8. 应用启动完成(ApplicationReadyEvent)

我们重点关注 第5~6步:Bean 的加载与实例化顺序


📌 二、Bean 加载的核心顺序机制

Spring Boot 中 Bean 的加载顺序由以下机制共同决定(优先级从高到低):


✅ 1. @DependsOn —— 强制依赖顺序(最高优先级)

🧩 作用:

强制指定当前 Bean 必须在另一个(或多个)Bean 初始化之后再初始化,无视其他顺序规则。

📝 示例:
@Component
public class DatabaseInitializer {public DatabaseInitializer() {System.out.println("✅ DatabaseInitializer 初始化");}
}@Component
@DependsOn("databaseInitializer") // 强制要求先初始化 databaseInitializer
public class CacheWarmUpService {public CacheWarmUpService() {System.out.println("✅ CacheWarmUpService 初始化(必须在 DatabaseInitializer 之后)");}
}

💡 输出顺序一定为:

✅ DatabaseInitializer 初始化
✅ CacheWarmUpService 初始化
⚠️ 注意:
  • @DependsOn 是“硬性规定”,即使被依赖的 Bean 是懒加载或条件加载,也会被强制提前初始化。
  • 支持多个依赖:@DependsOn({"a", "b"})

✅ 2. @Order / Ordered 接口 —— 定义执行顺序(常用于监听器、拦截器、过滤器)

🧩 作用:

定义组件的逻辑执行顺序,值越小优先级越高。常用于:

  • ApplicationListener
  • WebMvcConfigurer
  • Filter
  • HandlerInterceptor
  • CommandLineRunner
📝 示例:
@Component
@Order(1) // 优先级最高
public class FirstRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("🏃 第一个执行的 Runner");}
}@Component
@Order(2)
public class SecondRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("🏃 第二个执行的 Runner");}
}

💡 输出:

🏃 第一个执行的 Runner
🏃 第二个执行的 Runner
⚠️ 注意:
  • @Order 不影响 Bean 的创建顺序,只影响“执行顺序”。
  • 默认值 Ordered.LOWEST_PRECEDENCE(Integer.MAX_VALUE),即最低优先级。

✅ 3. @AutoConfigureBefore / @AutoConfigureAfter —— 控制自动配置类加载顺序

🧩 作用:

控制自动配置类(@Configuration)之间的加载顺序,确保某个配置在另一个之前/之后被处理。

📝 示例:
@Configuration
@AutoConfigureBefore(DataSourceAutoConfiguration.class) // 在数据源配置前加载
public class CustomDataSourcePreConfig {@Beanpublic CustomPropertySource customPropertySource() {System.out.println("🔧 在 DataSourceAutoConfiguration 之前加载");return new CustomPropertySource();}
}
⚠️ 注意:
  • 仅对 @Configuration 类有效。
  • 用于解决自动配置之间的依赖关系(如:你的 Starter 依赖 DataSource,必须在其后加载)。

✅ 4. @Conditional* 系列注解 —— 条件化加载(影响“是否加载”,间接影响顺序)

🧩 作用:

根据条件决定是否加载某个 Bean 或配置类。虽然不直接控制顺序,但未满足条件的组件不会被加载,自然也不会参与顺序竞争。

📝 示例:
@Configuration
@ConditionalOnProperty(name = "my.feature.enabled", havingValue = "true")
public class ConditionalConfig {@Beanpublic MyService myService() {return new MyService(); // 仅当配置开启时才创建}
}

如果 my.feature.enabled=false,则 MyService 根本不会被创建,自然也不会参与初始化顺序。


✅ 5. 构造器注入 / @Autowired —— 依赖驱动顺序(Spring 默认机制)

🧩 作用:

Spring 默认按依赖关系决定初始化顺序:被依赖的 Bean 会先初始化

📝 示例:
@Service
public class UserService {public UserService() {System.out.println("👤 UserService 初始化");}
}@Service
public class OrderService {private final UserService userService;public OrderService(UserService userService) { // 依赖 UserServicethis.userService = userService;System.out.println("🛒 OrderService 初始化");}
}

💡 输出:

👤 UserService 初始化
🛒 OrderService 初始化
⚠️ 注意:
  • 这是 Spring 最自然的顺序机制。
  • 避免循环依赖(A 依赖 B,B 依赖 A),否则启动失败。

✅ 6. @Lazy —— 延迟初始化(最后加载)

🧩 作用:

标记 Bean 为懒加载,只有在第一次被注入或调用时才初始化,启动时不加载。

📝 示例:
@Service
@Lazy // 启动时不初始化
public class ExpensiveService {public ExpensiveService() {System.out.println("💸 昂贵服务初始化(延迟)");}
}@Service
public class NormalService {private final ExpensiveService expensiveService;public NormalService(ExpensiveService expensiveService) {this.expensiveService = expensiveService;System.out.println("✅ NormalService 初始化");}public void useExpensive() {// 第一次调用时才会触发 ExpensiveService 初始化expensiveService.doSomething();}
}

💡 启动时输出:

✅ NormalService 初始化

调用 useExpensive() 时输出:

💸 昂贵服务初始化(延迟)

📌 三、实战:综合优先级排序表

机制作用范围优先级说明
@DependsOnBean 实例化⭐⭐⭐⭐⭐ 最高强制指定依赖顺序,无视其他规则
构造器注入 / @AutowiredBean 实例化⭐⭐⭐⭐Spring 默认依赖驱动顺序
@Conditional*Bean 加载决策⭐⭐⭐决定“是否加载”,间接影响顺序
@AutoConfigureBefore/After自动配置类⭐⭐控制配置类加载顺序
@Order / Ordered执行顺序不影响创建顺序,只影响执行顺序
@LazyBean 实例化最低延迟到首次使用时才初始化

一句话总结优先级:
@DependsOn > 依赖注入 > 条件装配 > 自动配置顺序 > @Order > @Lazy


📌 四、查看实际加载顺序的方法

方法1:启用调试日志

application.yaml 中添加:

logging:level:org.springframework: DEBUGorg.springframework.boot.autoconfigure: TRACE

启动时会输出详细 Bean 加载顺序:

Creating shared instance of singleton bean 'userService'
Creating shared instance of singleton bean 'orderService'

方法2:使用 BeanFactoryPostProcessor 打印顺序

@Component
public class BeanInitializationOrderPrinter implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {String[] beanNames = beanFactory.getBeanDefinitionNames();System.out.println("=== BeanDefinition 注册顺序 ===");for (int i = 0; i < beanNames.length; i++) {System.out.println((i + 1) + ". " + beanNames[i]);}}
}

⚠️ 注意:这只是注册顺序,不是初始化顺序!


方法3:使用 SmartInitializingSingleton 查看初始化完成顺序

@Component
public class InitializationOrderLogger implements SmartInitializingSingleton {@Overridepublic void afterSingletonsInstantiated() {System.out.println("✅ 所有单例 Bean 初始化完成");}
}

📌 五、实战建议与最佳实践

✅ 1. 优先使用自然依赖注入顺序

  • 让 Spring 自动管理依赖顺序是最安全、最可维护的方式。
  • 避免过度使用 @DependsOn,容易造成隐式耦合。

✅ 2. 自定义 Starter 必须使用 @AutoConfigureAfter

  • 如果你的 Starter 依赖 DataSourceRedisTemplate 等,务必在其后加载:
@Configuration
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MyStarterAutoConfiguration { ... }

✅ 3. 初始化任务使用 ApplicationRunner + @Order

  • 不要用 @PostConstruct 做复杂初始化,改用 ApplicationRunner
@Component
@Order(1)
public class DataInitRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {// 此时所有 Bean 已初始化完毕,安全执行业务逻辑}
}

✅ 4. 避免循环依赖

  • 使用 @Lazy 或重构设计解决循环依赖。
  • 启动报错:The dependencies of some of the beans... form a cycle

✅ 5. 敏感资源初始化使用 @DependsOn

  • 如:缓存预热必须在数据库初始化之后:
@Service
@DependsOn("databaseMigrationService")
public class CachePreloadService { ... }

📌 六、完整示例:模拟真实项目启动顺序

// 1. 数据库迁移(必须最先执行)
@Component
public class DatabaseMigrationService {public DatabaseMigrationService() {System.out.println("1️⃣ DatabaseMigrationService 初始化");}
}// 2. 缓存预热(必须在数据库之后)
@Component
@DependsOn("databaseMigrationService")
public class CacheWarmUpService {public CacheWarmUpService() {System.out.println("2️⃣ CacheWarmUpService 初始化");}
}// 3. 用户服务(自然依赖)
@Service
public class UserService {public UserService() {System.out.println("3️⃣ UserService 初始化");}
}// 4. 订单服务(依赖用户服务)
@Service
public class OrderService {public OrderService(UserService userService) {System.out.println("4️⃣ OrderService 初始化");}
}// 5. 启动后执行任务
@Component
@Order(1)
public class StartupTaskRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) {System.out.println("5️⃣ StartupTaskRunner 执行");}
}// 6. 懒加载服务
@Service
@Lazy
public class ReportService {public ReportService() {System.out.println("6️⃣ ReportService 初始化(延迟)");}
}

💡 启动输出:

1️⃣ DatabaseMigrationService 初始化
2️⃣ CacheWarmUpService 初始化
3️⃣ UserService 初始化
4️⃣ OrderService 初始化
5️⃣ StartupTaskRunner 执行

ReportService 只有在首次调用时才会输出。


✅ 总结

掌握 Spring Boot 组件加载顺序,你就能:

  • ✅ 精准控制 Bean 初始化时机
  • ✅ 避免启动失败和循环依赖
  • ✅ 优化启动性能(延迟加载)
  • ✅ 开发健壮的自定义 Starter
  • ✅ 调试复杂启动问题

记住这个口诀:

“依赖决定顺序,条件决定有无,DependsOn 是老大,Lazy 最后才出场”


如需我为你分析某个具体场景的加载顺序(如整合 Redis、RabbitMQ、Security 等),欢迎继续提问!

祝你成为 Spring Boot 启动大师!🚀

http://www.dtcms.com/a/406056.html

相关文章:

  • Spring Boot 事件发布与监听 观察者模式的实际应用
  • Sui Stack Messaging SDK:为 Web3 打造可编程通信
  • 光谱相机的未来趋势
  • 【Java后端】《Spring Boot Starter 原理详解》博客
  • 设计与绘制一个网站首页同学录wordpress
  • Vue2的生命周期
  • MySQL学习笔记04:MySQL InnoDB存储引擎核心机制深度解析
  • 中国企业网站建设响应式网站管理
  • 遇到不会的事,先写一写
  • 心理咨询 网站模版嘉兴网站建设技术开发
  • 【面试】Kafka / RabbitMQ / ActiveMQ
  • 新网站建设的工作总结文化网站建设需要的功能
  • 11.WPF 的命令处理事件--参数介绍
  • 旅游管理虚拟仿真实训室:打通理论与实践壁垒
  • FreeLong-无需训练即可延长视频生成时长
  • Lynx:新一代个性化视频生成模型,单图即可生成视频,重新定义身份一致性与视觉质量
  • 关于机器视觉中的”果冻效应“讲解:全局曝光 vs 卷帘曝光
  • 如何做百度的网站网站开发技术的雏形 cgi
  • 织梦医院网站源码6731官方网站下载
  • Transformer模型/注意力机制/目标检测/语义分割/图神经网络/强化学习/生成式模型/自监督学习/物理信息神经网络等
  • 公司网站 域名seo快速提高网站转化率
  • Planner Agent 和 PlanReAct 的区别
  • Google Play合规指南:您的应用所使用的原生库不支持 16 KB 内存页面大小.快速解决
  • 什么是覆盖索引?PostgreSQL 是否支持覆盖索引?
  • 谨慎地迭代函数所收到的参数 (Effective Python 第31条)
  • ESP32 NTC热敏电阻测温全攻略:从ADC采样到线性插值算法详解
  • 介绍 τ-bench:一个评估语言智能体在真实场景中与人、工具、规则交互能力的新基准
  • 网站模版建设教程效果好网站建设哪家好
  • 20-for循环案例练习
  • 华策影视 AIGC 实战:剧本分镜 1 小时生成,影视创作告别熬夜改稿时代