Spring Boot 的 @Configuration 指定加载顺序实践
项目里配置类一多,就容易产生「谁先加载」的疑问:两个配置类里定义了同类型的 Bean 会不会冲突?A 配置里注入的 List 顺序为什么每次不一样?本文整理用 @Order 指定配置类顺序的写法,并重点澄清它到底能控制什么、不能控制什么 —— 后者往往才是踩坑的地方。
一、用 @Order 指定配置类优先级
可以使用 Spring 框架提供的 @Order 注解来指定配置类的优先级。@Order 注解接受一个整数值,数字越小优先级越高,意味着该配置类会更早被处理。
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@Configuration
@Order(1) // 数字越小,优先级越高
public class FirstConfig {
// 配置内容
}
@Configuration
@Order(2)
public class SecondConfig {
// 配置内容
}补一个稍完整、能直接看到效果的例子:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@Configuration
@Order(1) // 优先级高
public class FirstConfig {
@Bean
public FirstService firstService() {
return new FirstService();
}
}
@Configuration
@Order(2) // 优先级低
public class SecondConfig {
@Bean
public SecondService secondService() {
return new SecondService();
}
}等价的另一种写法:实现 Ordered 接口
@Order 本质上是 Ordered 接口的注解形式。当顺序值需要动态计算(例如从配置项、环境变量推导)时,直接实现接口更灵活:
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
@Configuration
public class FirstConfig implements Ordered {
@Override
public int getOrder() {
return 1; // 数字越小,优先级越高
}
}注意:不要把 @Order 和 getOrder() 同时写在一个类上,Spring 的排序比较器会优先读取注解值,容易造成「改了代码没生效」的困惑。
二、@Order 实际影响什么
把 @Order 加在 @Configuration 类上,能产生下面这些可靠的效果:
配置类被解析、处理的先后顺序。Spring 在处理配置类时会用
AnnotationAwareOrderComparator对配置类候选排序,@Order小的配置类先被处理,其内部的@Bean定义也先被注册(先注册的 BeanDefinition 不会覆盖后注册的,这点在处理同类型 Bean 时很关键)。同一类型多个 Bean 注入集合时的顺序。这是最实用的场景。向
List<T>、T[]注入多个实现时,Spring 会按 order 值排序。@Import进来的配置类同样参与排序,所以拆分配置模块时可以靠@Order表达依赖关系。
集合注入顺序示例
public interface MyCustomizer {
void customize();
}
@Configuration
public class CustomizerConfig {
@Bean
@Order(1)
public MyCustomizer aCustomizer() {
return () -> System.out.println("A customizer");
}
@Bean
@Order(2)
public MyCustomizer bCustomizer() {
return () -> System.out.println("B customizer");
}
}
@Service
public class MyService {
private final List<MyCustomizer> customizers;
// 注入顺序固定为 A → B,与 @Order 一致
public MyService(List<MyCustomizer> customizers) {
this.customizers = customizers;
}
}提示:这里 @Order 是写在 @Bean 方法上的。工厂方法上的 @Order 会被 Spring 解析为该 Bean 的排序值,用于集合注入;把它写在 @Configuration 类上则只影响配置类本身。
三、@Order 不影响什么(重点)
这是最容易误解的部分。以下情况 @Order 帮不上忙:
一句话总结:@Order 管的是「排序」,不是「先后执行的时序」。如果 FirstConfig 的 Bean 依赖 SecondConfig 的 Bean,那么无论 @Order 写多少,被依赖的那个一定先创建 —— 依赖关系永远优先于 @Order。
四、真正控制初始化顺序的手段
1. @DependsOn —— 显式声明依赖
没有注入关系但需要保证创建顺序时使用:
@Bean
@DependsOn("dataSourceInitializer")
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}被 @DependsOn 指定的 Bean 会先于当前 Bean 创建。
2. 构造器注入 / @Autowired —— 利用天然依赖
最推荐的方式:让依赖关系真实存在,Spring 自然按拓扑顺序创建,不需要任何顺序注解。
3. 想要「全部 Bean 就绪后再做事」
实现
SmartInitializingSingleton#afterSingletonsInstantiated();或监听
ApplicationReadyEvent/ContextRefreshedEvent。
这类场景不要试图用 @Order 解决。
五、自动配置类的顺序
如果你是写 Starter、或者想调整 Spring Boot 自动配置的先后,用的是另一套注解,与普通 @Configuration 上的 @Order 不是一回事:
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService() {
return new MyService();
}
}关键区别:@AutoConfigureOrder 只对通过自动配置机制(imports / spring.factories)注册的类生效;写在业务代码里的普通 @Configuration 上加它没有效果,那种场景要用 @Order。
六、常见问题
1. 两个配置类定义了同类型 Bean,谁生效?
默认情况下后注册的定义会因 spring.main.allow-bean-definition-overriding 的取值而不同:Boot 2.1 之后默认为 false,重复定义会直接启动失败。使用 @ConditionalOnMissingBean 才是消除冲突的正解,靠 @Order 抢注册顺序并不可靠。
2. 用 @Order 能控制 @PropertySource 的覆盖关系吗?
属性源的加入顺序确实会跟随配置类的处理顺序,但不建议依赖这一点做配置覆盖 —— 这类需求请使用多环境配置(application-{profile}.yml)、spring.config.import 或命令行参数,语义更明确、也不受重构影响。
3. @Order 的值该取多少?
Spring 提供两个常量参考:Ordered.HIGHEST_PRECEDENCE(Integer.MIN_VALUE)与 Ordered.LOWEST_PRECEDENCE(Integer.MAX_VALUE,也是未标注时的默认值)。业务代码建议从 0 开始,按 10、20、30 的步长递增,留出插入空间,避免后期调整时大面积改值。
4. order 值相同时会怎样?
顺序不确定,取决于注册顺序,不同环境、不同版本可能不一致。需要保证顺序的场景,请为每个配置分配不同值。
七、小结
@Order接受整数,数字越小优先级越高,配置类会更早被处理;也可以改为实现Ordered接口。可靠效果只有两个:配置类处理顺序与集合注入顺序(后者需要把
@Order写在@Bean方法上)。它不控制 Bean 的初始化时序 —— 那是依赖关系的地盘,用构造器注入或
@DependsOn来管。自动配置类的先后请用
@AutoConfigureBefore/@AutoConfigureAfter/@AutoConfigureOrder,普通业务配置类上写这些无效。业务代码里给 order 留出步长(10、20、30),顺序才长期可控。
评论区