理解 Spring Order 註解

我們都知道 @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 的順序”這個結論,其實這個結論是有問題的。反駁很簡單,直接上代碼,將上面的 CatCat3@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 進行排序的。值得一提的是這裏 ApplicationRunnerCommandLineRunner 是被同等看待的。

總結

最後做一個總結,@Order 註解並不能控制 Bean 的加載順序,而是 Spring 提供的一個排序屬性,我們可以根據這個屬性做相應的調整從而滿足我們的需求。

References

  • https://www.jianshu.com/p/37edf9389814

歡迎關注公衆號
​​​​​​在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章