欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺析Spring如何控制Bean的加載順序

 更新時間:2025年07月09日 15:33:54   作者:冰糖心書房  
在大多數(shù)情況下,我們不需要手動控制 Bean 的加載順序,因?yàn)?nbsp;Spring 的 IoC 容器足夠智能,但在某些特殊場景下,這種隱式的依賴關(guān)系可能不存在,下面我們就來看看Spring如何控制Bean的加載順序吧

在大多數(shù)情況下,我們不需要手動控制 Bean 的加載順序,因?yàn)?Spring 的 IoC 容器足夠智能。

核心原則:依賴驅(qū)動加載

Spring IoC 容器會構(gòu)建一個依賴關(guān)系圖(Dependency Graph)。如果 Bean A 依賴于 Bean B(例如,A 的構(gòu)造函數(shù)需要一個 B 類型的參數(shù)),Spring 會保證在創(chuàng)建 Bean A 之前,Bean B 已經(jīng)被完全創(chuàng)建和初始化好了。

@Service
public class ServiceA {
    private final ServiceB serviceB;

    // 因?yàn)?ServiceA 在構(gòu)造時需要 ServiceB,
    // Spring 保證 serviceB 會先被創(chuàng)建。
    @Autowired
    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
        System.out.println("ServiceA 初始化了,此時 ServiceB 已經(jīng)可用。");
    }
}

@Service
public class ServiceB {
    public ServiceB() {
        System.out.println("ServiceB 初始化了。");
    }
}

// 輸出結(jié)果:
// ServiceB 初始化了。
// ServiceA 初始化了,此時 ServiceB 已經(jīng)可用。

但是,在某些特殊場景下,這種隱式的依賴關(guān)系可能不存在,我們需要強(qiáng)制一個特定的初始化順序。這時就需要手動控制。

手動控制 Bean 加載順序的方法

以下是幾種常用的手動控制方法。

方法 1:使用@DependsOn(最直接、最明確)

@DependsOn 注解可以直接聲明一個 Bean 在初始化之前,必須先初始化另一個Bean。這用于處理沒有直接依賴關(guān)系(即 A 類中沒有 B 類的字段引用),但存在邏輯上或“副作用”上的依賴(比如 B 必須先初始化數(shù)據(jù)庫表,A 才能去操作它)。

場景:一個 DatabaseInitializer 負(fù)責(zé)創(chuàng)建數(shù)據(jù)庫表,而一個 DataImporter 負(fù)責(zé)向這些表中導(dǎo)入數(shù)據(jù)。DataImporter 并沒有直接注入 DatabaseInitializer,但它依賴于 DatabaseInitializer 的工作先完成。

// Bean A: 數(shù)據(jù)導(dǎo)入器
@Component
@DependsOn("databaseInitializer") // <-- 關(guān)鍵點(diǎn)
public class DataImporter {
    public DataImporter() {
        System.out.println("DataImporter: 開始導(dǎo)入數(shù)據(jù)...(此時數(shù)據(jù)庫表一定已創(chuàng)建)");
        // ...導(dǎo)入邏輯...
    }
}

// Bean B: 數(shù)據(jù)庫初始化器
@Component("databaseInitializer") // 注意Bean的名字
public class DatabaseInitializer {
    public DatabaseInitializer() {
        System.out.println("DatabaseInitializer: 正在創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu)...");
        // ...建表邏輯...
    }
}

輸出順序保證是:

  • DatabaseInitializer: 正在創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu)...
  • DataImporter: 開始導(dǎo)入數(shù)據(jù)...(此時數(shù)據(jù)庫表一定已創(chuàng)建)

@DependsOn 可以接受一個字符串?dāng)?shù)組,來依賴多個 Bean:@DependsOn({"beanA", "beanB"})。

方法 2:使用@EventListener監(jiān)聽容器事件(適用于后處理邏輯)

有時候,你需要的不是“在另一個 Bean 之前加載”,而是“在所有 Bean 都加載完畢之后,再執(zhí)行某些邏輯”。這對于緩存預(yù)熱、啟動后執(zhí)行一次性任務(wù)等場景非常有用。

你可以通過監(jiān)聽 ContextRefreshedEvent 來實(shí)現(xiàn)。這個事件會在 Spring 容器完成所有 Bean 的初始化和配置后發(fā)布。

場景:在應(yīng)用啟動后,需要將數(shù)據(jù)庫中的熱門商品預(yù)加載到 Redis 緩存中。

@Component
public class CacheWarmer {

    private final ProductService productService;
    private final RedisTemplate<String, Object> redisTemplate;

    public CacheWarmer(ProductService productService, RedisTemplate<String, Object> redisTemplate) {
        this.productService = productService;
        this.redisTemplate = redisTemplate;
    }

    @EventListener(ContextRefreshedEvent.class) // <-- 關(guān)鍵點(diǎn)
    public void onApplicationEvent() {
        System.out.println("容器已啟動完畢,開始預(yù)熱緩存...");
        // 此時,productService 和 redisTemplate 肯定已經(jīng)準(zhǔn)備就緒
        List<Product> hotProducts = productService.findHotProducts();
        redisTemplate.opsForValue().set("hot_products", hotProducts);
        System.out.println("緩存預(yù)熱完成!");
    }
}

這種方式保證了你的邏輯在整個應(yīng)用程序準(zhǔn)備就緒后才執(zhí)行,是一種非常安全和解耦的順序控制。

方法 3:在 Spring Boot 中使用@AutoConfigureAfter/@AutoConfigureBefore

這兩種注解是 Spring Boot 自動配置模塊專用的。它們用來控制**配置類(@Configuration)**之間的加載順序,而不是普通的 Component Bean。

當(dāng)你編寫自己的 starter 或自動配置時,這個方法至關(guān)重要。

場景:你的自動配置 MyDataSourceAutoConfiguration 必須在 Spring Boot 的 DataSourceAutoConfiguration 之后執(zhí)行,以確保數(shù)據(jù)源已經(jīng)存在。

@Configuration
@AutoConfigureAfter(DataSourceAutoConfiguration.class) // <-- 關(guān)鍵點(diǎn)
public class MyCustomConfiguration {
    
    @Bean
    public MyService myService(DataSource dataSource) {
        // 因?yàn)橛?@AutoConfigureAfter,這里的 dataSource 肯定已經(jīng)被
        // DataSourceAutoConfiguration 配置好了。
        return new MyService(dataSource);
    }
}

一個常見的誤區(qū):@Order

@Order 注解或 Ordered 接口完全不能控制 Bean 的加載(初始化)順序!

這是一個非常非常常見的誤解。

@Order 的作用是對集合中的元素進(jìn)行排序。當(dāng)你將多個相同接口的實(shí)現(xiàn)注入到一個 List 中時,@Order 用來決定它們在這個 List 中的順序。

例子:你有多個過濾器 Filter,你想控制它們的執(zhí)行順序。

@Component
@Order(1) // 序號小的優(yōu)先
class LoggingFilter implements Filter { ... }

@Component
@Order(2)
class SecurityFilter implements Filter { ... }

@Service
public class MyProcessor {
    @Autowired
    private List<Filter> filters; // 注入一個Filter的List

    public void process() {
        // 因?yàn)?@Order,可以保證 LoggingFilter 在 SecurityFilter 之前執(zhí)行
        filters.forEach(Filter::doFilter);
    }
}

在這個例子中,@Order 決定了 filters 列表中的元素順序,但它不影響 LoggingFilterSecurityFilter 這兩個 Bean 本身的初始化順序。它們的初始化順序仍然由 Spring 的依賴圖決定。

總結(jié)

方法用途優(yōu)點(diǎn)缺點(diǎn)/適用范圍
隱式依賴 (構(gòu)造函數(shù))[首選] 定義 Bean 之間的直接依賴關(guān)系最自然、最符合 IoC 思想,代碼清晰無法處理沒有直接引用的“副作用”依賴
@DependsOn[推薦] 強(qiáng)制指定初始化順序,處理“副作用”依賴意圖明確,直接解決問題引入了對 Bean 名字的字符串依賴,略有侵入性
@EventListener在所有 Bean 初始化后執(zhí)行邏輯高度解耦,適用于應(yīng)用啟動后的任務(wù)不是控制 Bean 之間的順序,而是控制邏輯的執(zhí)行時機(jī)
@AutoConfigure...控制 Spring Boot 自動配置類的順序Spring Boot 自動配置的標(biāo)準(zhǔn)方式只對 @Configuration 類有效
@Order[非加載順序] 對集合中的 Bean 進(jìn)行排序控制業(yè)務(wù)邏輯的執(zhí)行順序完全不能控制 Bean 的加載或初始化順序

最佳實(shí)踐:

  • 95% 的情況,請使用構(gòu)造函數(shù)注入來表達(dá)依賴關(guān)系,讓 Spring 自動管理加載順序。這是最干凈、最可靠的方式。
  • 當(dāng)你確實(shí)需要處理沒有直接引用的邏輯依賴時(如初始化任務(wù)),@DependsOn 是你的首選工具。
  • 當(dāng)你需要在整個應(yīng)用啟動完成后執(zhí)行代碼時,@EventListener(ContextRefreshedEvent.class) 是最優(yōu)雅的方案。
  • 永遠(yuǎn)不要試圖用 @Order 去控制 Bean 的加載順序。

到此這篇關(guān)于淺析Spring如何控制Bean的加載順序的文章就介紹到這了,更多相關(guān)Spring控制Bean加載順序內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java超詳細(xì)大文件分片上傳代碼

    Java超詳細(xì)大文件分片上傳代碼

    文件上傳是一個很常見的功能。在項(xiàng)目開發(fā)過程中,我們通常都會使用一些成熟的上傳組件來實(shí)現(xiàn)對應(yīng)的功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧<BR>
    2022-06-06
  • springboot集成shiro權(quán)限管理簡單實(shí)現(xiàn)

    springboot集成shiro權(quán)限管理簡單實(shí)現(xiàn)

    這篇文章主要介紹了springboot集成shiro權(quán)限管理簡單實(shí)現(xiàn),文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-08-08
  • Struts2學(xué)習(xí)筆記(1)-入門教程

    Struts2學(xué)習(xí)筆記(1)-入門教程

    本文是一個Struts2的簡單入門教程,比較簡單,希望能給大家做一個參考。
    2016-06-06
  • Java線程池流程編排運(yùn)用實(shí)戰(zhàn)源碼

    Java線程池流程編排運(yùn)用實(shí)戰(zhàn)源碼

    這篇文章主要介紹了Java線程池流程編排運(yùn)用實(shí)戰(zhàn)源碼,就在流程引擎的基礎(chǔ)上運(yùn)用?ThreadPoolExecutor,使用線程池實(shí)現(xiàn)?SpringBean?的異步執(zhí)行
    2022-03-03
  • java中的Consumer、Supply如何實(shí)現(xiàn)多參數(shù)?

    java中的Consumer、Supply如何實(shí)現(xiàn)多參數(shù)?

    Java的Consumer接口只能接受一個參數(shù),但可以通過自定義接口、使用Tuple或嵌套結(jié)構(gòu)來實(shí)現(xiàn)對多個參數(shù)的處理,對于Supplier接口,它不能接受參數(shù),但可以通過自定義BiSupplier、結(jié)合Function或封裝參數(shù)為對象來實(shí)現(xiàn)對兩個參數(shù)并返回一個值的功能
    2024-11-11
  • java算法題解LeetCode30包含min函數(shù)的棧實(shí)例

    java算法題解LeetCode30包含min函數(shù)的棧實(shí)例

    這篇文章主要為大家介紹了java算法題解LeetCode30包含min函數(shù)的棧實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • java.lang.Void類的解析與使用詳解

    java.lang.Void類的解析與使用詳解

    這篇文章主要介紹了java.lang.Void類的解析與使用詳解,文中涉及到了java.lang.integer類的源碼,分場景給大家介紹的非常詳細(xì),給大家補(bǔ)充介紹java.lang.Void 與 void的比較及使用,需要的朋友可以參考下
    2017-12-12
  • springboot如何為web層添加統(tǒng)一請求前綴

    springboot如何為web層添加統(tǒng)一請求前綴

    這篇文章主要介紹了springboot如何為web層添加統(tǒng)一請求前綴,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java中IO流 RandomAccessFile類實(shí)例詳解

    Java中IO流 RandomAccessFile類實(shí)例詳解

    這篇文章主要介紹了Java中IO流 RandomAccessFile類實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • SpringMVC訪問靜態(tài)資源的三種方式小結(jié)

    SpringMVC訪問靜態(tài)資源的三種方式小結(jié)

    這篇文章主要介紹了SpringMVC訪問靜態(tài)資源的三種方式小結(jié),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02

最新評論