關於Spring中@Order 、@AutoConfigureBefore等與順序相關注解的誤區
1、@Order注解並非一定會給你的bean排序
@Order注解表示排序,但是它不能決定@Configuration或者是@Bean注解的實例化順序。決定Spring里bean實例化或者注入順序的一般情況下是bean之間的依賴關系。這個依賴關系一般是類的構造函數的參數、類屬性或者@DependsOn注解來控制。
以下是錯誤的演示,Test1Config和Test2Config兩個配置類的實例化,包括Apple和Orange的實例化都無法用@Order注解控制,調換順序后可以發現實例化的順序不會變化,因為他們之間並沒有依賴關系。
@Configuration
@Order(100)
public class Test1Config {
public Test1Config() {
System.out.println("Test1Config 構造");
}
@Bean
public Apple apple(){
System.out.println("准備new Apple ...");
return new Apple();
}
}
@Configuration
@Order(0)
public class Test2Config {
public Test2Config() {
System.out.println("Test2Config 構造");
}
@Bean
public Orange orange(){
System.out.println("准備new Orange ...");
return new Orange();
}
}
2、那么如何控制配置類以及Bean的實例化順序呢?
在實際項目中,我們確實有需要控制某幾個類的注入IOC容器的順序,一般如果有直接依賴關系可以用類屬性或者構造函數方式實現,一般推薦類屬性注入,因為Spring並沒有解決構造函數注入的循環依賴問題,所以沒有十分的把握不要采用構造函數注入。
如果Bean之間其實沒有直接引用依賴關系,但是又要控制兩個Bean之間的注入順序,可以使用@DependsOn注解。比如上面的例子,假設Test2Config需要先實例化注入到IOC容器,可以采用如下方式:
@Configuration
@DependsOn("test2Config")
public class Test1Config {
public Test1Config() {
System.out.println("Test1Config 構造");
}
@Bean
public Apple apple(){
System.out.println("准備new Apple ...");
return new Apple();
}
}
3、那么@Order注解如何正確使用?
@Order注解只會給Spring實現其排序功能的組件或者接口中發揮作用,又或者由開發者自定義其順序的實現。比如:Spring的接口CommandLineRunner就可以支持@Order注解,在我們實現多個CommandLineRunner接口的時候,如果對其執行順序有要求的時候,就可以使用@Order注解。
@Component
@Order(160)
public class AppleCommand implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("AppleCommand run ...");
}
}
@Component
@Order(150)
public class OrangeCommand implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("OrangeCommand run ...");
}
}
調整兩段代碼@Order的大小,可以調整其執行先后順序。
對於@Order注解,可以參考官方解釋,深刻理解其含義和應用。
NOTE: Since Spring 4.0, annotation-based ordering is supported for many kinds of components in Spring, even for collection injection where the order values of the target components are taken into account (either from their target class or from their @Bean method). 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 @DependsOn declarations (influencing a runtime-determined dependency graph).
4、@AutoConfigureBefore是給自動配置排序用的
@AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureOrder這三個注解是給Springboot自動配置類排序使用的,注意是自動配置類,並非是普通的配置類。
Springboot的自動配置需要在jar包目錄下META-INF/spring.factories
文件中進行類名配置。類似:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.wood.lib.autoconfigure.AXXXAutoConfiguration,\
com.wood.lib.autoconfigure.BXXXWebAutoConfiguration
注:自動配置類只能通過這種方式加載,確保它們定義在一個特殊的package中,特別是不能成為組件掃描的目標。
這樣你可以使用@AutoConfigureAfter
或@AutoConfigureBefore
注解為配置類指定特定的順序。如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
@AutoConfigureAfter(BXXXWebAutoConfiguration.class)
public class AXXXAutoConfiguration {
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
...
return registration;
}
... ...
}