SpringBoot排序之Order注解

@Order 注解定义了类、方法和字段的优先级(排序情况),value 是可选的,默认为Ordered.LOWEST_PRECEDENCE,即最低优先级。表示 Ordered 接口中的 order 属性。

目前看到的 @Order 注解都是用在类上的,没有看到过用在方法和字段上的,包括 Spring 自有类 DefaultErrorAttributes、LogbackLoggingSystem.Factory 等。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {

/**
* 优先级排序值,value 的值越大,优先级越低
* 默认值为 Ordered.LOWEST_PRECEDENCE(Integer.MAX_VALUE)
* @see Ordered#getOrder()
*/
int value() default Ordered.LOWEST_PRECEDENCE;

}

从 Spring 4.0 开始,Spring 中的很多组件都支持基于 Order 注解的排序,但是使用了该注解后,不会影响bean的加载顺序,Bean的加载顺序是由依赖关系和 @DependsOn 注解来决定的。

我们定义两个bean:OrderBean1和OrderBean2,然后通过 @Bean 注入到容器中,启动 SpringBoot 应用,看看打印情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class OrderBean1 {

public OrderBean1() {
System.out.println("init Order Bean 1");
}
}

public class OrderBean2 {

public OrderBean2() {
System.out.println("init Order Bean 2");
}
}

@Configuration
@Order(200)
public class BeanConfig {

public BeanConfig() {
System.out.println("bean config");
}

@Bean
public OrderBean1 orderBean1() {
return new OrderBean1();
}
}


@Configuration
@Order(100)
public class BeanConfig2 {

public BeanConfig2() {
System.out.println("bean config 2");
}

@Bean
public OrderBean2 orderBean2() {
return new OrderBean2();
}
}

虽然 BeanConfig 和 BeanConfig2 都加了 Order 注解,且 BeanConfig2 的优先级更高,但是实际的日志打印是

1
2
3
4
bean config
bean config 2
init Order Bean 1
init Order Bean 2

BeanConfig2 并没有优先于 BeanConfig 进行加载。

如果想要 BeanConfig2 优先加载,可以借助 @DependsOn 注解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
@DependsOn("beanConfig2")
public class BeanConfig1 {

public BeanConfig1() {
System.out.println("bean config 1");
}

@Bean
@DependsOn("orderBean2")
public OrderBean1 orderBean1() {
return new OrderBean1();
}
}

日志打印为:

1
2
3
4
bean config 2
bean config 1
init Order Bean 2
init Order Bean 1

那 Order 注解在什么场景下才能使用呢?在进行执行顺序的排序时使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public interface OrderBean {
void order();
}

@Order(200)
public class OrderBean1 implements OrderBean{

@Override
public void order() {
System.out.println("order bean1 ---> order command");
}
}

@Order(100)
public class OrderBean2 implements OrderBean{

@Override
public void order() {
System.out.println("order bean2 ---> order command");
}
}

@Configuration
public class BeanCommandConfig {

public BeanCommandConfig(List<OrderBean> orderBeans) {
orderBeans.forEach(orderBean -> orderBean.order());
}
}

输出信息为

1
2
order bean2 --->  order command
order bean1 ---> order command

可以看到,先删除了 bean2 的内容,再输出了 bean1 的内容。

SpringBoot中的典型应用就是 CommandLineRunner。

Interface used to indicate that a bean should run when it is contained within a SpringApplication. Multiple CommandLineRunner beans can be defined within the same application context and can be ordered using the Ordered interface or @Order annotation.

翻译:多个CommandLineRunner bean 可以在同一个应用程序上下文中定义,并且可以使用Ordered接口或@Order注释进行排序

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
@Order(5)
public class Runner1 implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("runner 1 start");
}
}

@Component
@Order(1)
public class Runner2 implements CommandLineRunner {

@Override
public void run(String... args) throws Exception {
System.out.println("runner 2 start");
}
}

启动 SpringBoot 工程,查看控制台输出

1
2
runner 2 start
runner 1 start

可以看到,两个 Runner 按照配置的优先级执行,先运行 Runner2,再运行 Runner1。

从 Spring 4.1 开始,jakarta.annotation.Priority 可在一些排序场景中替代 @Order 的作用,但是在修饰单个元素时,@Priority 可能具有其他语义。

除了这两个注解外,也可以实现 Ordered 接口,来设置优先级信息,相对使用注解的方式更加灵活。