@Order注解,你理解錯(cuò)了!
環(huán)境:SpringBoot3.2.5
1. 簡介
@Order注解是Spring框架中用于定義Bean執(zhí)行順序的優(yōu)先級的一個(gè)注解,它位于spring-core包下。這個(gè)注解可以應(yīng)用在類、方法和字段上,其作用是影響B(tài)ean注入到集合中的順序,但不影響B(tài)ean的加載和實(shí)例化順序。@Order 注解接受一個(gè)整數(shù)值作為參數(shù),數(shù)值越小表示優(yōu)先級越高。需要注意的是,@Order 注解或Ordered接口不能決定Bean的加載順序。
注意:不會(huì)影響實(shí)例化的順序,實(shí)例化的順序是由你注冊(通過掃描時(shí),先發(fā)現(xiàn)的A,那就先實(shí)例化A)。
那 @Order 注解到底能用在哪些地方呢?接下來,我們將介紹一些常用場景下使用 @Order 注解的有效方法。
2. 實(shí)戰(zhàn)案例
2.1 注入類型為集合
當(dāng)我們在注入一個(gè)集合類型時(shí)(有多個(gè)相同類型,如一個(gè)接口多個(gè)實(shí)現(xiàn)),我們可以通過@Order注解來控制它們在集合中的順序。
// 定義接口
public interface DAO {
public void save() ;
}
// 下面3個(gè)實(shí)現(xiàn)類
@Component
public class A implements DAO {
public void save() {
System.out.println("A...") ;
}
}
@Component
public class B implements DAO {
public void save() {
System.out.println("B...") ;
}
}
@Component
public class C implements DAO {
public void save() {
System.out.println("C...") ;
}
}
// 集合注入
@Resource
private List<DAO> daos ;
public void print() {
for (DAO dao : daos) {
dao.save() ;
}
}
運(yùn)行上面程序,執(zhí)行結(jié)果如下:
A...
B...
C...
分別添加@Order注解
@Order(2)
public class A...
@Order(1)
public class B...
@Order(0)
public class C...
再次運(yùn)行
C...
B...
A...
這里的集合還可以是Array類型。都支持排序。
除了使用@Order注解,你還可以實(shí)現(xiàn)Ordered接口。同時(shí),bean的注冊方式也可以是通過配置類@Bean也可以添加@Order注解。
2.2 事件監(jiān)聽ApplicationListener
@Component
@Order(-1)
public class ListenerA implements ApplicationListener<PackEvent> {
@Override
public void onApplicationEvent(PackEvent event) {
System.out.println("A Listener...") ;
}
}
@Component
@Order(-2)
public class ListenerB implements ApplicationListener<PackEvent> {
@Override
public void onApplicationEvent(PackEvent event) {
System.out.println("B Listener...") ;
}
}
當(dāng)發(fā)布PackEvent事件后,打印順序如下:
B Listener...
A Listener...
同樣你可以實(shí)現(xiàn)Ordered接口。
2.3 Application/CommandLineRunner
*Runner接口會(huì)在整個(gè)Spring Boot啟動(dòng)完成最后一個(gè)階段(Spring容器已經(jīng)完成加載),如下
圖片
圖片
@Component
@Order(0)
public class RunnerA implements CommandLineRunner {
public void run(String... args) throws Exception {
System.out.println("A Runner...") ;
}
}
@Component
@Order(-1)
public class RunnerB implements CommandLineRunner {
public void run(String... args) throws Exception {
System.out.println("B Runner...") ;
}
}
輸出結(jié)果
B Runner...
A Runner...
一樣通過配置類注冊或者是實(shí)現(xiàn)Ordered接口都可以。
2.4 BeanPostProcessor
這是個(gè)Bean處理器(實(shí)例化Bean對象前后執(zhí)行回調(diào)),我們只能通過實(shí)現(xiàn)Ordered接口來控制順序。
public class APostProcessor implements BeanPostProcessor, Ordered {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("A BeanPostProcessor") ;
return bean ;
}
public int getOrder() {
return -1 ;
}
}
public class BPostProcessor implements BeanPostProcessor, Ordered {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("B BeanPostProcessor") ;
return bean ;
}
public int getOrder() {
return -2 ;
}
}
執(zhí)行輸出結(jié)果
B BeanPostProcessor
A BeanPostProcessor
目前不支持基于 @Order 注解方式。
除了實(shí)現(xiàn)Ordered接口外,你還可以實(shí)現(xiàn)PriorityOrdered接口。
2.5 BeanFactoryPostProcessor
該接口與上面的BeanPostProcessor基本一致,你只能通過實(shí)現(xiàn)Ordered接口的方式控制順序。
2.6 @Aspect切面
@Aspect
@Order(-1)
public static class AspectA {
@Pointcut("execution(* *(..))")
private void a() {}
@Before("a()")
public void before() {
System.out.println("A before...") ;
}
}
@Aspect
@Order(-2)
public static class AspectB {
@Pointcut("execution(* *(..))")
private void b() {}
@Before("b()")
public void before() {
System.out.println("B before...") ;
}
}
切面執(zhí)行
B before...
A before...
Demo save...
切面除了使用@Order還可以實(shí)現(xiàn)Ordered接口。
以上列出了我們工作中比較常用的一些場景使用上可以應(yīng)用@Order注解或?qū)崿F(xiàn)Ordered接口。而在Spring Boot環(huán)境下還有很多其它的一些情況都是支持排序的。
2.7 其它
FailureAnalyzer、ApplicationContextInitializer、ErrorPageRegistrar、ErrorViewResolver等。
其實(shí)如果是通過如下方式獲取的,都是支持排序的
// 只要是通過該方式獲取對象的,都是支持排序的
SpringFactoriesLoader#load(Class type)