Spring中@order註解用法實戰教程

前言

@order註解是spring-core包下的一個註解,@Order的作用是定義Spring IOC容器中Bean的執行順序的優先級(這裡的順序也可以理解為存放到容器中的先後順序)。開發過程當中有時候經常會出現配置依賴關系,例如註入A對象使用瞭

@ConditionalOnBean(B.class),意思是要求容器當中必須存在B.class的實例的時候,才會進行註入A。這時候我們就必須保證B對象在註入A對象前進行註入。

一、觀察@order源碼

(1)源碼當中有三個元註解:

  • @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}): 使用范圍接口、類、枚舉、註解、方法、字段
  • @Retention(RetentionPolicy.RUNTIME): @Retention是用來修飾註解的生命周期的,RetentionPolicy.RUNTIME代表的是不僅被保存到class文件中,jvm加載class文件之後,仍然存在;一直有效!
  • @Documented: @Documented和@Deprecated註解長得有點像,@Deprecated是用來標註某個類或者方法不建議再繼續使用,@Documented隻能用在註解上,如果一個註解@B,被@Documented標註,那麼被@B修飾的類,生成Javadoc文檔時,會顯示@B。

(2)屬性:

@order當中隻要一個value屬性,而且還是int類型,值越低優先級越高,默認值是Ordered.LOWEST_PRECEDENCE,表示最低優先級(輸給任何其他指定的順序值)。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
    int value() default 2147483647;
}

官網註釋:https://github.com/spring-projects/spring-framework/blob/main/spring-core/src/main/java/org/springframework/core/annotation/Order.java

二、@order實戰

(1)自定義兩個配置類

我們要求Config2先進行加載,然後通過@order來排序測試一下

@Configuration
public class Config1 {

    public Config1() {
        System.out.println("Config1構建瞭");
    }
}

@Configuration
public class Config2 {

    public Config2() {
        System.out.println("Config2構建瞭");
    }
}

(2)啟動項目測試:默認是先創建的Config1後創建的Config2

(3)既然order可以控制加載順序,那我們來試驗一下,然後讓Config2 先加載

@Configuration
@Order(2)
public class Config1 {

    public Config1() {
        System.out.println("Config1構建瞭");
    }
}

@Configuration
@Order(1)
public class Config2 {

    public Config2() {
        System.out.println("Config2構建瞭");
    }
}

但是好像沒什麼卵用

(4)分析原因

目前這兩個是在同包情況下不起作用。

於是進行分開瞭

分開之後竟然生效瞭

(5)但是分開也是將Config2放到瞭上面的包當中,於是我又改成瞭Config1放到最上面,這樣進行測試,結果又失效瞭

(6)於是我又放在瞭同包下,將Config2命名為A開頭的,這樣他就放到瞭最上面,於是這樣同樣也生效瞭。

期間我還嘗試著將@Configuration都改為使用@Component,結果仍然不變。

得出結論:@order指定加載順序還跟類的命名和存放位置有關!假如有Config1Config2兩個類在一個包下,要求是Config2先加載:

  • 這時候設置Config2@order值就算是小於Config1@order值同樣也是Config1先加載。
  • 如果同包情況下可以重新命名Config2,隻要在Config1上面就行。
  • 或者拆開不同包也可以,但是Config2所在的包也必須比Config1所呆的包上面。

這樣才能保證@Order生效!

三、@order失效原因

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-constructor-injection

最關鍵的一句話:您可以在目標類級別和@Bean方法上聲明@Order註釋,可能針對的是單個bean定義(如果多個定義使用同一個bean類)。@Order值可能會影響註入點的優先級,但請註意,它們不會影響單例啟動順序,這是由依賴關系和@DependsOn聲明確定的正交關註。

我理解的註入點的優先級應該是指的存放容器的先後順序,也就是他並不會影響啟動順序。

四、解決排序問題

我們不可能每次遇到這種問題又是改名又是換包的,所以,springboot提供瞭如下三個註解可以控制順序:

  • @AutoConfigureAfter:當前配置類在指定配置類之後執行
  • @AutoConfigureBefore:當前配置類在指定配置類之前執行
  • @AutoConfigureOrder:指定優先級,數值越小,優先級越高。

(1)首先將代碼改回原來的樣子

(2)在Config2使用@AutoConfigureBefore(Config1.class),代表的是在config1加載前進行加載

@Configuration
public class Config1 {

    public Config1() {
        System.out.println("Config1構建瞭");
    }
}

@Configuration
@AutoConfigureBefore(Config1.class)
public class Config2 {

    public Config2() {
        System.out.println("Config2構建瞭");
    }
}

(3)輸出結果,顯然還是沒生效

可能有時候走瞭運給你一種錯覺還真的配置成功瞭。實際上這種方式是不可行的,以上三個註解隻有針對自動配置類才會生效。

在autoconfigure包下就有spring.factories,這個文件配置瞭自動配置類,springboot會讀取這個文件的,我們也可以在自己項目上定義spring.factories,這樣我們的配置類對於@AutoConfigureAfter註解就可以生效瞭。

(4)自定義spring.factories

第一行是固定的,後面的就是全類名,雖然隻有Config2使用瞭註解,但是需求是和Config1進行排序,所以這兩個都得加。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.gzl.cn.springbootcache.config.Config2,\
com.gzl.cn.springbootcache.config.Config1

(5)測試,成功解決

五、排序源碼分析

針對於@AutoConfigure那三個註解原理:其實關鍵的代碼還是在AutoConfigurationImportSelector中,將自動配置類從spring.factories加載出來之後會根據條件排序(隻有自動配置類!),在selectImports()方法中最後一行代碼如下:

緊接著會走到這個地方,實際上是分瞭三步排序:

  • 先按照文件名字母排序
  • 按照@AutoConfigureOrder進行排序
  • 按照 @AutoConfigureBefore和@AutoConfigureAfter排序

從上面配置的順序可以知道,最終決定權還是在@AutoConfigureAfter、@AutoConfigureBefore這兩個註解。

當我們不設置spring.factories的時候,這裡面壓根都沒有這兩個類!

六、@AutoConfigureOrder

這種也是可以的!當然前提也是需要配置spring.factories

@Configuration
@AutoConfigureOrder(2)
public class Config1 {

    public Config1() {
        System.out.println("Config1構建瞭");
    }
}

@Configuration
@AutoConfigureOrder(1)
public class Config2 {

    public Config2() {
        System.out.println("Config2構建瞭");
    }
}

總結

到此這篇關於Spring中@order註解用法的文章就介紹到這瞭,更多相關Spring @order註解用法內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: