我們都知道 @Order
是控制優先級的,越小優先級越高,那麼問題來了,是控制什麼的優先級呢(雖然不能太“槓”,但是個人認爲有時候還是得咬文嚼字)。有博客(相關鏈接見文末)的說法是“註解 @Order
的作用是定義 Spring 容器加載 Bean 的順序”,其相關示例如下:
定義了三個 Bean:
@Component
@Order(1)
public class Cat {
private String catName;
private int age;
public Cat() {
System.out.println("Order:1");
}
//getter/setter
}
@Component
@Order(2)
public class Cat2 {
private String catName;
private int age;
public Cat2() {
System.out.println("Order:2");
}
//getter/setter
}
@Component
@Order(3)
public class Cat3 {
private String catName;
private int age;
public Cat3() {
System.out.println("Order:3");
}
public String getCatName() {
return catName;
}
//getter/setter
}
然後啓動 Spring 容器,發現輸出:
Order:1
Order:2
Order:3
於是得出了“註解 @Order
的作用是定義 Spring 容器加載 Bean 的順序”這個結論,其實這個結論是有問題的。反駁很簡單,直接上代碼,將上面的 Cat
和 Cat3
的 @Order
調整一下:將 Cat3
的 @Order
設置爲 1,無參構造輸出“Order 1”,Cat
的 @Order
設置爲 3,無參構造輸出“Order 3”。啓動 Spring 容器,輸出:
Order:3
Order:2
Order:1
這時候發現 @Order
爲 1 的類反而後初始化了。其實在 @Order
中的註釋已經說的很清楚了:
* While such order values may influence priorities
* at injection points, please be aware that they do not influence singleton startup
* order which is an orthogonal concern determined by dependency relationships and
* {@code @DependsOn} declarations (influencing a runtime-determined dependency graph).
也就是說,其實 @Order
並不能控制 Bean 的加載順序,其實 Spring 在處理的時候,是加載到誰了,就開始加載,然後如果有相關依賴,就加載依賴,是一種層層遞歸的方式。
依賴注入順序
根據註釋,可以看出 @Order
對注入的順序會有影響,首先將代碼恢復至最開始的狀態(即 Cat
-> Order = 1,Cat2
-> Order = 2 …)。然後定義了一個 Animal
類,CatX
都繼承 Animal
。增加一個配置類:
@Configuration
public class CatConfiguration {
@Autowired
private void setCats(List<Animal> animals) {
animals.forEach(c -> System.out.println("--->"+OrderUtils.getOrder(c.getClass())));
}
}
這裏採用依賴注入的方式注入了所有的 Animal
,輸出結果:
Order:3
Order:2
Order:1
--->1
--->2
--->3
原因是因爲在依賴注入的過程中,org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveMultipleBeans 方法會對依賴注入的參數進行一個排序:
其實就是根據 @Order 進行排序的:
@Nullable
private Comparator<Object> adaptDependencyComparator(Map<String, ?> matchingBeans) {
Comparator<Object> comparator = getDependencyComparator();
if (comparator instanceof OrderComparator) {
return ((OrderComparator) comparator).withSourceProvider(
createFactoryAwareOrderSourceProvider(matchingBeans));
}
else {
return comparator;
}
}
調用鏈路:
過濾器執行順序
我們一般會這麼指定過濾器:
@Configuration
public class FilterConfiguration {
@Bean
public FilterRegistrationBean filter1() {
FilterRegistrationBean<Filter1> registration = new FilterRegistrationBean<>();
registration.addUrlPatterns("/*");
registration.setOrder(1);
registration.setFilter(new Filter1());
return registration;
}
@Bean
public FilterRegistrationBean filter2() {
FilterRegistrationBean<Filter2> registration = new FilterRegistrationBean<>();
registration.addUrlPatterns("/*");
registration.setOrder(1);
registration.setFilter(new Filter2());
return registration;
}
}
也是通過 Order 來實現的,因爲 FilterRegistrationBean 實現了 Ordered 接口,在 org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize
方法中會調用 org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#getServletContextInitializerBeans
方法,在構建 ServletContextInitializerBeans
集合的時候會根據 order
屬性進行排序:
@SafeVarargs
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
addServletContextInitializerBeans(beanFactory);
addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
CommandListener 執行順序
CommandListener
可以幫助我們在 org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String...)
方法完成前進行相應的操作,也是因爲 org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String...)
方法會調用 org.springframework.boot.SpringApplication#callRunners
方法:
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
發現也是根據 order
進行排序的。值得一提的是這裏 ApplicationRunner
和 CommandLineRunner
是被同等看待的。
總結
最後做一個總結,@Order
註解並不能控制 Bean 的加載順序,而是 Spring 提供的一個排序屬性,我們可以根據這個屬性做相應的調整從而滿足我們的需求。
References
- https://www.jianshu.com/p/37edf9389814
歡迎關注公衆號