最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot啟動(dòng)后的初始化數(shù)據(jù)加載原理解析與實(shí)戰(zhàn)

 更新時(shí)間:2024年11月22日 10:22:02   作者:不惑_  
本文主要圍繞?Spring?Boot?啟動(dòng)后的初始化數(shù)據(jù)加載展開,介紹了初始化任務(wù)的基本需求,包括全局配置加載、數(shù)據(jù)庫表初始化等,闡述了多種初始化加載方式,分析了它們的優(yōu)缺點(diǎn),需要的朋友可以參考下

系統(tǒng)初始化操作是一個(gè)非常常見的需求。通常,應(yīng)用在啟動(dòng)后需要執(zhí)行一些重要的初始化任務(wù),例如加載全局配置、初始化數(shù)據(jù)庫表、預(yù)熱緩存、啟動(dòng)后臺(tái)任務(wù)等。而如何選擇合適的技術(shù)方案,在不同的場景下保證初始化任務(wù)的高效執(zhí)行,尤其在多實(shí)例的分布式部署中,如何確保任務(wù)只執(zhí)行一次,成為我們?cè)陧?xiàng)目實(shí)戰(zhàn)中需要深入思考和優(yōu)化的關(guān)鍵問題。

本文將結(jié)合 Spring Boot 框架,從基礎(chǔ)的啟動(dòng)機(jī)制、核心技術(shù)原理到分布式環(huán)境下的復(fù)雜場景,帶領(lǐng)大家逐步深入理解如何通過不同方式進(jìn)行啟動(dòng)后的初始化任務(wù)執(zhí)行。最終,我們會(huì)通過一個(gè)項(xiàng)目實(shí)戰(zhàn)例子,演示如何確保初始化任務(wù)在分布式部署環(huán)境中只執(zhí)行一次,解決多實(shí)例下的任務(wù)重復(fù)執(zhí)行問題。

初始化任務(wù)的基本需求

這些任務(wù)有一個(gè)共同的特性:它們通常只需要在應(yīng)用啟動(dòng)時(shí)執(zhí)行一次。因此,選擇一個(gè)合適的機(jī)制來執(zhí)行這些初始化操作,并且在分布式環(huán)境中確保任務(wù)不會(huì)被重復(fù)執(zhí)行,是至關(guān)重要的。包括但不限于:

  • 全局配置的加載:如從數(shù)據(jù)庫、配置文件或遠(yuǎn)程服務(wù)加載全局的應(yīng)用參數(shù)。
  • 數(shù)據(jù)庫表初始化:例如檢查并創(chuàng)建缺失的數(shù)據(jù)庫表、插入初始數(shù)據(jù)等。
  • 緩存預(yù)熱:應(yīng)用啟動(dòng)后立即加載部分常用數(shù)據(jù)到緩存中,減少首次訪問的延遲。
  • 后臺(tái)任務(wù)啟動(dòng):啟動(dòng)如消息隊(duì)列監(jiān)聽、定時(shí)任務(wù)調(diào)度等長期運(yùn)行的后臺(tái)服務(wù)。
  • 系統(tǒng)健康檢查:確保關(guān)鍵依賴服務(wù)(如數(shù)據(jù)庫、消息隊(duì)列、第三方服務(wù))在啟動(dòng)時(shí)正常工作。

啟動(dòng)后初始化加載的幾種方式

Spring Boot 提供了多種機(jī)制來處理應(yīng)用啟動(dòng)后的初始化任務(wù)。這些機(jī)制涵蓋了單機(jī)部署和分布式部署的需求,并且具有不同的執(zhí)行時(shí)機(jī)和適用場景。

PostConstruct 注解

@PostConstruct 是一種非常簡潔且常用的方式,它用于標(biāo)注在 Spring 管理的 Bean 完成依賴注入后自動(dòng)調(diào)用的方法。這種方式特別適合單個(gè) Bean 的初始化操作。Spring 在完成依賴注入后,自動(dòng)調(diào)用帶有 @PostConstruct 注解的方法,確保初始化邏輯在 Bean 初始化完成時(shí)執(zhí)行。

適用于需要在某個(gè)特定 Bean 初始化完成后執(zhí)行的任務(wù)。例如,某個(gè)服務(wù)類需要在加載時(shí)讀取配置文件或執(zhí)行特定的初始化操作。

@Component
public class MyService {
    @PostConstruct
    public void init() {
        // 初始化任務(wù),只執(zhí)行一次
        System.out.println("Bean 初始化后執(zhí)行");
    }
}

優(yōu)點(diǎn):

  • 簡單直觀:它是 Java EE 標(biāo)準(zhǔn)的一部分,使用非常簡單。只需要在一個(gè)方法上加上 @PostConstruct 注解,Spring 會(huì)在 Bean 初始化后自動(dòng)調(diào)用該方法。
  • 自動(dòng)調(diào)用:可以確保在 Spring 容器完成 Bean 的依賴注入后,自動(dòng)執(zhí)行初始化操作,不需要顯式調(diào)用。

缺點(diǎn):

  • 只能處理單個(gè) Bean 的初始化任務(wù)@PostConstruct 注解的方法只能作用于某個(gè)具體 Bean,在這個(gè) Bean 完成初始化時(shí)執(zhí)行。如果有多個(gè) Bean 需要初始化任務(wù),@PostConstruct 無法跨 Bean 控制執(zhí)行邏輯。

CommandLineRunner / ApplicationRunner

CommandLineRunner 和 ApplicationRunner 是 Spring Boot 中用于在應(yīng)用啟動(dòng)完成后執(zhí)行初始化任務(wù)的接口。兩者的區(qū)別在于傳遞的參數(shù)形式,CommandLineRunner 提供原始的 String[] 參數(shù),而 ApplicationRunner 封裝了啟動(dòng)參數(shù)的上下文信息。

CommandLineRunner:這個(gè)接口提供的是原始的 String[] 啟動(dòng)參數(shù),這些參數(shù)通常是應(yīng)用啟動(dòng)時(shí)傳遞給 Java 程序的命令行參數(shù)。如果你只關(guān)心應(yīng)用啟動(dòng)時(shí)的命令行參數(shù)并且需要直接操作它們,可以使用 CommandLineRunner。

ApplicationRunner:這個(gè)接口封裝了啟動(dòng)時(shí)的參數(shù),通過 ApplicationArguments 類提供對(duì)命令行參數(shù)的更高層次的訪問。如果你需要更多功能(例如獲取非標(biāo)準(zhǔn)格式的命令行參數(shù),處理帶有標(biāo)志的參數(shù)等),可以使用 ApplicationRunner。

優(yōu)點(diǎn)

  • 適用于全局任務(wù):無論是 CommandLineRunner 還是 ApplicationRunner,都能在所有 Bean 完成初始化之后執(zhí)行,確保應(yīng)用的啟動(dòng)邏輯是全局性的。
  • 自動(dòng)執(zhí)行:一旦實(shí)現(xiàn)了這些接口的類被注冊(cè)為 Spring Bean,Spring 會(huì)自動(dòng)調(diào)用它們的 run() 方法,任務(wù)執(zhí)行是自動(dòng)的,無需顯式調(diào)用。

缺點(diǎn)

  • 多個(gè)實(shí)現(xiàn)類的順序控制:如果項(xiàng)目中有多個(gè) CommandLineRunnerApplicationRunner 實(shí)現(xiàn)類,默認(rèn)情況下它們的執(zhí)行順序是不確定的。為了保證執(zhí)行順序,可以使用 @Order 注解明確指定執(zhí)行的優(yōu)先級(jí)。
  • 只能執(zhí)行一次:這類方法在 Spring Boot 應(yīng)用啟動(dòng)時(shí)執(zhí)行,并且默認(rèn)只執(zhí)行一次。如果你需要某些任務(wù)在應(yīng)用的生命周期內(nèi)多次執(zhí)行,這種方式不適用。

使用 CommandLineRunner

@Component
public class MyStartupRunner implements CommandLineRunner {
    @Override
    public void run(String... args) {
        // 這是應(yīng)用啟動(dòng)后要執(zhí)行的任務(wù)
        System.out.println("應(yīng)用啟動(dòng)后執(zhí)行初始化任務(wù)");
    }
}

使用 ApplicationRunner

@Component
public class MyAppStartupRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 獲取命令行參數(shù)
        System.out.println("應(yīng)用啟動(dòng)后執(zhí)行初始化任務(wù),獲取啟動(dòng)參數(shù):" + args.getOptionNames());
    }
}

運(yùn)行后我們會(huì)發(fā)現(xiàn),@Component 的執(zhí)行順序確實(shí)早于 ApplicationRunner 和 CommandLineRunner。

為什么是這個(gè)順序

**@Component注解的 Bean:**任何標(biāo)注為 @Component(或 @Service、@Repository、@Controller 等)的類會(huì)在 Spring Boot 應(yīng)用啟動(dòng)時(shí)被自動(dòng)掃描并實(shí)例化。這些 Bean 會(huì)在 Spring 容器啟動(dòng)的初期階段被創(chuàng)建和初始化,在應(yīng)用啟動(dòng)過程中最早被加載和執(zhí)行。

ApplicationRunner CommandLineRunner: 這兩個(gè)接口的實(shí)現(xiàn)類是 Spring Boot 特有的啟動(dòng)鉤子,它們?cè)谒蠤Component Bean 被創(chuàng)建和初始化之后執(zhí)行,但在 Spring Boot 完成應(yīng)用啟動(dòng)(即應(yīng)用的上下文已準(zhǔn)備好)后執(zhí)行。ApplicationRunner 會(huì)比 CommandLineRunner 早執(zhí)行,因?yàn)樗庋b了啟動(dòng)參數(shù)上下文的更多信息,所以它們的 run() 方法會(huì)在 Spring Boot 完成上下文刷新和 Bean 初始化之后執(zhí)行。

執(zhí)行順序控制

如果你的項(xiàng)目中有多個(gè)實(shí)現(xiàn)類,且它們都需要在應(yīng)用啟動(dòng)時(shí)執(zhí)行,使用 @Order 注解可以明確控制執(zhí)行順序。例如:

@Component
@Order(1)  // 設(shè)置優(yōu)先級(jí),數(shù)字越小優(yōu)先級(jí)越高
public class FirstStartupTask implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("第一個(gè)啟動(dòng)任務(wù)");
    }
}

@Component
@Order(2)
public class SecondStartupTask implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("第二個(gè)啟動(dòng)任務(wù)");
    }
}

這樣,F(xiàn)irstStartupTask 會(huì)在 SecondStartupTask 之前執(zhí)行。

Spring Boot 生命周期事件

Spring Boot 提供了事件驅(qū)動(dòng)的編程模型,可以幫助開發(fā)者在應(yīng)用生命周期的不同階段執(zhí)行特定的任務(wù)。通過監(jiān)聽 Spring 生命周期事件,開發(fā)者可以在精確的時(shí)機(jī)執(zhí)行初始化任務(wù)。這種方式適合需要確保任務(wù)執(zhí)行時(shí)機(jī)的場景,例如,某些任務(wù)必須等到應(yīng)用完全啟動(dòng)、數(shù)據(jù)庫連接已經(jīng)建立、服務(wù)已經(jīng)準(zhǔn)備好之后才能執(zhí)行。Spring Boot 中有很多生命周期事件,例如:

**ApplicationReadyEvent:**當(dāng)應(yīng)用完全啟動(dòng)并準(zhǔn)備好處理請(qǐng)求時(shí)觸發(fā)。此事件表示 Spring 應(yīng)用上下文已經(jīng)完全初始化,應(yīng)用已準(zhǔn)備好接收外部請(qǐng)求。適用于在應(yīng)用啟動(dòng)完成后立即執(zhí)行的初始化任務(wù),例如啟動(dòng)后臺(tái)服務(wù)、初始化緩存等。

ContextRefreshedEvent:當(dāng) Spring 上下文被初始化或刷新時(shí)觸發(fā)。這通常發(fā)生在 Spring Boot 啟動(dòng)過程中,用于標(biāo)志 Spring 容器準(zhǔn)備好并且所有 Bean 已初始化完畢。 - 適合在應(yīng)用啟動(dòng)時(shí)進(jìn)行一些預(yù)熱操作,如加載配置信息、初始化數(shù)據(jù)庫連接池等。

ApplicationStartedEvent:在 Spring Boot 應(yīng)用啟動(dòng)時(shí)觸發(fā),發(fā)生在 Spring 上下文加載之前,可以用于執(zhí)行一些早期的初始化任務(wù)。

ApplicationEnvironmentPreparedEvent:在 Spring Boot 啟動(dòng)過程中,當(dāng)應(yīng)用的 Environment 配置完成時(shí)觸發(fā)。這時(shí)還未初始化 Spring 容器,適用于一些基于環(huán)境配置的初始化。

ApplicationFailedEvent:如果 Spring Boot 啟動(dòng)失敗,這個(gè)事件會(huì)被觸發(fā)??梢杂脕硖幚響?yīng)用啟動(dòng)失敗后的清理或日志記錄。

監(jiān)聽 Spring 生命周期事件

Spring Boot 提供了 ApplicationListener 接口和 @EventListener 注解來監(jiān)聽這些生命周期事件。我們可以通過這兩種方式,在特定的時(shí)機(jī)執(zhí)行初始化任務(wù)。

1:使用 ApplicationListener 監(jiān)聽 ApplicationReadyEvent

通過實(shí)現(xiàn) ApplicationListener 接口,可以監(jiān)聽指定的事件,例如監(jiān)聽 ApplicationReadyEvent 來確保在應(yīng)用完全啟動(dòng)后執(zhí)行任務(wù)。

@Component
public class ApplicationListenerExample implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        // 應(yīng)用啟動(dòng)完成后執(zhí)行的任務(wù)
        System.out.println("應(yīng)用啟動(dòng)完成后執(zhí)行任務(wù)");
    }
}
  • 事件ApplicationReadyEvent 是 Spring Boot 應(yīng)用啟動(dòng)完成后發(fā)布的事件。
  • 觸發(fā)時(shí)機(jī):在所有 Bean 被初始化之后,并且 Spring 上下文已經(jīng)完全加載并準(zhǔn)備好時(shí),ApplicationReadyEvent 會(huì)被觸發(fā)。

2:使用 @EventListener 注解監(jiān)聽事件

Spring 5 引入了 @EventListener 注解,它提供了更簡潔的方式來監(jiān)聽事件,不需要顯式實(shí)現(xiàn) ApplicationListener 接口。

@Component
public class EventListenerExample {
    @EventListener(ApplicationReadyEvent.class)
    public void handleApplicationReady() {
        // 應(yīng)用啟動(dòng)完成后執(zhí)行的任務(wù)
        System.out.println("使用 @EventListener 在應(yīng)用啟動(dòng)后執(zhí)行任務(wù)");
    }
}
  • 事件@EventListener(ApplicationReadyEvent.class) 表示監(jiān)聽 ApplicationReadyEvent 事件。
  • 觸發(fā)時(shí)機(jī)@EventListener 注解的方法會(huì)在 ApplicationReadyEvent 觸發(fā)時(shí)執(zhí)行,適用于應(yīng)用完全啟動(dòng)后的任務(wù)。

事件驅(qū)動(dòng)機(jī)制的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  • 精確控制執(zhí)行時(shí)機(jī):通過監(jiān)聽特定的生命周期事件,開發(fā)者可以精確地控制初始化任務(wù)的執(zhí)行時(shí)機(jī),確保任務(wù)只在應(yīng)用處于某種狀態(tài)時(shí)執(zhí)行。例如,ApplicationReadyEvent 事件保證任務(wù)只在應(yīng)用完全啟動(dòng)后執(zhí)行。
  • 解耦應(yīng)用邏輯:使用事件監(jiān)聽機(jī)制,開發(fā)者不需要直接在應(yīng)用啟動(dòng)過程中顯式調(diào)用初始化任務(wù)。事件驅(qū)動(dòng)可以幫助將應(yīng)用的啟動(dòng)邏輯與任務(wù)執(zhí)行邏輯解耦,使得代碼更清晰、更易維護(hù)。
  • 處理復(fù)雜的初始化邏輯:如果初始化邏輯比較復(fù)雜,或者有多個(gè)任務(wù)需要按特定順序執(zhí)行,事件機(jī)制可以提供靈活的控制。例如,ContextRefreshedEvent 可以確保一些初始化任務(wù)在 Spring 上下文初始化完成后執(zhí)行。

缺點(diǎn):

  • 依賴事件觸發(fā):事件機(jī)制的缺點(diǎn)是任務(wù)的執(zhí)行依賴于事件的觸發(fā),這要求開發(fā)者對(duì) Spring 事件機(jī)制有所了解。如果應(yīng)用啟動(dòng)過程中沒有觸發(fā)預(yù)期的事件,初始化任務(wù)可能會(huì)錯(cuò)過執(zhí)行時(shí)機(jī)。
  • 調(diào)試難度較大:如果任務(wù)執(zhí)行依賴于特定的事件觸發(fā),在調(diào)試時(shí)可能需要特別關(guān)注事件的觸發(fā)順序和事件的監(jiān)聽邏輯,否則可能會(huì)導(dǎo)致任務(wù)無法按預(yù)期執(zhí)行。
  • 學(xué)習(xí)成本:對(duì)于沒有使用過 Spring 事件機(jī)制的開發(fā)者來說,需要花費(fèi)一些時(shí)間學(xué)習(xí)和理解事件發(fā)布與監(jiān)聽的原理,尤其是在多事件場景下,理解事件的傳播與監(jiān)聽順序。

使用 @Bean(initMethod) 定義自定義初始化方法

在 Spring 中,@Bean(initMethod) 提供了一種自定義 Bean 初始化方法的方式。通過這種機(jī)制,開發(fā)者可以為特定的 Bean 指定一個(gè)初始化方法,該方法會(huì)在 Spring 容器完成該 Bean 的依賴注入和實(shí)例化后自動(dòng)執(zhí)行。通常,這種方法用于處理一些復(fù)雜的初始化操作,例如初始化數(shù)據(jù)庫連接、加載外部配置、啟動(dòng)后臺(tái)任務(wù)等。

適用于需要復(fù)雜初始化的 Bean:當(dāng) Bean 的初始化需要執(zhí)行復(fù)雜的操作(例如調(diào)用外部 API、執(zhí)行文件加載、數(shù)據(jù)庫連接初始化等)時(shí),可以通過 initMethod 指定一個(gè)初始化方法,而不需要在 Bean 類中使用 @PostConstruct 注解或 CommandLineRunner 等方式。

外部配置控制初始化邏輯:@Bean(initMethod = "init") 允許將 Bean 的初始化方法與外部配置綁定,使得開發(fā)者能夠更靈活地控制 Bean 的初始化過程。

@Configuration
public class AppConfig {
    @Bean(initMethod = "init")
    public MyService myService() {
        return new MyService();
    }
}

public class MyService {
    // 自定義初始化邏輯
    public void init() {
        System.out.println("自定義初始化方法");
    }
}
  • @Bean(initMethod = "init"):這里,@Bean 注解為 MyService 定義了一個(gè)初始化方法 init。當(dāng) MyService Bean 被創(chuàng)建并且依賴注入完成后,init 方法會(huì)被自動(dòng)調(diào)用。
  • init() 方法:MyService 類中定義了一個(gè)名為 init() 的方法,這個(gè)方法在 Bean 完成初始化后會(huì)被自動(dòng)調(diào)用。通常,這個(gè)方法包含了一些額外的初始化邏輯,比如外部資源的加載或初始化。

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn):

  • 靈活性@Bean(initMethod) 允許開發(fā)者靈活指定初始化方法的名稱,適合用于需要在 Bean 初始化時(shí)執(zhí)行某些特定任務(wù)的場景。與 @PostConstructCommandLineRunner 等方法相比,@Bean(initMethod) 提供了更多的控制選項(xiàng),尤其在復(fù)雜的應(yīng)用程序中。
  • 支持復(fù)雜初始化邏輯:當(dāng) Bean 的初始化過程比較復(fù)雜(例如需要加載配置、啟動(dòng)線程、或者處理數(shù)據(jù)庫連接等),可以通過 initMethod 方法指定特定的初始化邏輯。
  • 與 Spring 配置文件解耦:在使用 @Configuration 類中配置 Bean 時(shí),initMethod 提供了一種不依賴于 Bean 類本身的初始化方式。它有助于將 Bean 的配置與實(shí)現(xiàn)分離。

缺點(diǎn):

  • 可讀性略差:相比 @PostConstructCommandLineRunner 等注解的方式,@Bean(initMethod) 需要開發(fā)者在配置類中明確指定初始化方法的名稱,這可能導(dǎo)致代碼的可讀性略差,尤其是在項(xiàng)目中有多個(gè)配置類和多個(gè)初始化方法時(shí),開發(fā)者可能需要更加小心地管理這些方法。
  • 不適合所有類型的初始化:對(duì)于一些簡單的 Bean 初始化任務(wù),例如基本的配置加載或基礎(chǔ)的數(shù)據(jù)預(yù)處理,使用 @Bean(initMethod) 可能顯得過于冗余。此時(shí),使用 @PostConstructCommandLineRunner 等方式更為簡潔和直接。
  • 初始化方法依賴 Spring 容器的管理@Bean(initMethod) 中指定的初始化方法必須是由 Spring 容器管理的 Bean。在某些情況下(例如某些純 Java 對(duì)象或手動(dòng)創(chuàng)建的 Bean),可能無法直接使用該方法進(jìn)行初始化。

綜合項(xiàng)目實(shí)戰(zhàn)

需求說明

假設(shè)我們有一個(gè)應(yīng)用,要求在啟動(dòng)時(shí)執(zhí)行以下初始化任務(wù):

  • 從數(shù)據(jù)庫或遠(yuǎn)程服務(wù)加載全局配置。
  • 初始化一些數(shù)據(jù)庫表(如果不存在)。
  • 預(yù)熱緩存。
  • 啟動(dòng)后臺(tái)任務(wù)(如定時(shí)任務(wù))。
  • 在多實(shí)例的分布式環(huán)境中,確保某些初始化任務(wù)只執(zhí)行一次(如初始化某些基礎(chǔ)數(shù)據(jù)、加載配置等)。

方案設(shè)計(jì)

我們將使用 Spring Boot 的不同初始化方式結(jié)合分布式鎖來確保任務(wù)只執(zhí)行一次。具體使用以下技術(shù):

  • @PostConstruct:用于簡單的初始化任務(wù)。
  • CommandLineRunner:用于全局任務(wù),在應(yīng)用啟動(dòng)后執(zhí)行。
  • Spring 事件機(jī)制:用于精確控制任務(wù)的執(zhí)行時(shí)機(jī)。
  • 分布式鎖(基于 Redis):確保在多實(shí)例環(huán)境中,任務(wù)只執(zhí)行一次。
  • 自定義 @Bean(initMethod):為需要特殊初始化邏輯的 Bean 指定初始化方法。

1. 項(xiàng)目結(jié)構(gòu)

src
└── main
    ├── java
    │   └── com
    │       └── example
    │           ├── Application.java
    │           ├── config
    │           │   └── AppConfig.java
    │           ├── initializer
    │           │   ├── AppStartupRunner.java
    │           │   ├── CacheInitializer.java
    │           │   └── DistributedLockInitializer.java
    │           └── service
    │               └── MyService.java
    └── resources
        └── application.properties

2. application.properties

# Redis 配置
spring.redis.host=localhost
spring.redis.port=6379

# 數(shù)據(jù)庫配置(假設(shè)使用 MySQL)
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=root

3. 主應(yīng)用入口類 Application.java

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

4. 配置類 AppConfig.java

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.example.service.MyService;

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public MyService myService() {
        return new MyService();
    }
}

5. 服務(wù)類 MyService.java

package com.example.service;

public class MyService {

    public void init() {
        // 這里可以執(zhí)行復(fù)雜的初始化操作,如外部API調(diào)用等
        System.out.println("執(zhí)行 MyService 的自定義初始化方法");
    }
}

6. 啟動(dòng)初始化任務(wù)類 AppStartupRunner.java

package com.example.initializer;

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class AppStartupRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        // 應(yīng)用啟動(dòng)后執(zhí)行的初始化任務(wù)
        System.out.println("執(zhí)行應(yīng)用啟動(dòng)時(shí)的全局初始化任務(wù)");
    }
}

7. 緩存初始化類 CacheInitializer.java

package com.example.initializer;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class CacheInitializer {

    @PostConstruct
    public void init() {
        // 預(yù)熱緩存邏輯,例如加載常用數(shù)據(jù)到緩存
        System.out.println("執(zhí)行緩存預(yù)熱任務(wù)");
    }
}

8. 分布式鎖初始化類 DistributedLockInitializer.java

package com.example.initializer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class DistributedLockInitializer implements ApplicationListener<ApplicationReadyEvent> {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        // 獲取分布式鎖,確保初始化任務(wù)只執(zhí)行一次
        Boolean success = redisTemplate.opsForValue().setIfAbsent("init_lock", "locked");
        if (Boolean.TRUE.equals(success)) {
            // 模擬執(zhí)行初始化任務(wù)(如初始化數(shù)據(jù)表、加載配置等)
            System.out.println("執(zhí)行初始化任務(wù):初始化數(shù)據(jù)庫或加載基礎(chǔ)數(shù)據(jù)");
            // 這里可以執(zhí)行初始化數(shù)據(jù)庫表、加載默認(rèn)數(shù)據(jù)等任務(wù)
        } else {
            System.out.println("任務(wù)已經(jīng)在其他實(shí)例中執(zhí)行,當(dāng)前實(shí)例跳過任務(wù)");
        }
    }
}

9.關(guān)鍵技術(shù)和設(shè)計(jì)

  • @PostConstruct:用于緩存預(yù)熱任務(wù),在 Bean 初始化完成后立即執(zhí)行。
  • CommandLineRunner:全局任務(wù)的執(zhí)行方式,確保應(yīng)用啟動(dòng)后執(zhí)行某些初始化操作。
  • 事件驅(qū)動(dòng)機(jī)制(ApplicationReadyEvent):精確控制任務(wù)執(zhí)行的時(shí)機(jī),確保任務(wù)在應(yīng)用完全啟動(dòng)并準(zhǔn)備好后執(zhí)行。
  • 分布式鎖(基于 Redis):確保初始化任務(wù)在多實(shí)例部署的環(huán)境中只執(zhí)行一次。通過 StringRedisTemplate 實(shí)現(xiàn) Redis 的分布式鎖,防止在多個(gè)實(shí)例中重復(fù)執(zhí)行相同任務(wù)。
  • @Bean(initMethod):為特定的 Bean 指定自定義初始化方法,適合需要復(fù)雜初始化的場景。

10.運(yùn)行效果

  • 當(dāng)應(yīng)用啟動(dòng)時(shí),CommandLineRunner 會(huì)執(zhí)行全局初始化任務(wù),確保應(yīng)用啟動(dòng)后的任務(wù)被執(zhí)行。
  • CacheInitializer 會(huì)在 Bean 初始化完成后執(zhí)行緩存預(yù)熱。
  • DistributedLockInitializer 會(huì)通過 Redis 分布式鎖確保初始化任務(wù)只執(zhí)行一次。若任務(wù)已經(jīng)在其他實(shí)例中執(zhí)行,當(dāng)前實(shí)例跳過該任務(wù)。

關(guān)鍵要點(diǎn)

  • 在單機(jī)環(huán)境中,@PostConstruct 和 CommandLineRunner 等方式已足夠滿足大多數(shù)初始化需求。
  • 在分布式環(huán)境下,必須確保初始化任務(wù)只執(zhí)行一次,推薦使用基于 Redis 的分布式鎖來避免重復(fù)執(zhí)行。
  • Spring 提供的事件機(jī)制和 @Bean(initMethod) 允許開發(fā)者靈活控制任務(wù)的執(zhí)行時(shí)機(jī)和邏輯,滿足復(fù)雜業(yè)務(wù)場景的需求。

通過靈活運(yùn)用這些技術(shù)和方法,開發(fā)者可以確保應(yīng)用啟動(dòng)時(shí)的初始化任務(wù)高效、安全地執(zhí)行,避免在多實(shí)例環(huán)境中產(chǎn)生重復(fù)執(zhí)行的情況。最終,這些方案能夠幫助開發(fā)者在確保系統(tǒng)穩(wěn)定性和性能的同時(shí),提高系統(tǒng)的可維護(hù)性和擴(kuò)展性。

以上就是SpringBoot啟動(dòng)后的初始化數(shù)據(jù)加載原理解析與實(shí)戰(zhàn)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot初始化數(shù)據(jù)加載的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 基于SpringBoot和Dify實(shí)現(xiàn)流式響應(yīng)輸出

    基于SpringBoot和Dify實(shí)現(xiàn)流式響應(yīng)輸出

    這篇文章主要為大家詳細(xì)介紹了如何基于SpringBoot和Dify實(shí)現(xiàn)流式響應(yīng)輸出效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下
    2025-03-03
  • java實(shí)現(xiàn)簡單銀行管理系統(tǒng)

    java實(shí)現(xiàn)簡單銀行管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡單銀行管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • Java中的遞歸方法示例介紹

    Java中的遞歸方法示例介紹

    大家好,本篇文章主要講的是Java中的遞歸方法示例介紹,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • springcloud-gateway集成knife4j的示例詳解

    springcloud-gateway集成knife4j的示例詳解

    這篇文章主要介紹了springcloud-gateway集成knife4j的示例詳解,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • IDEA配置Tomcat后,控制臺(tái)tomcat?catalina?log出現(xiàn)亂碼問題

    IDEA配置Tomcat后,控制臺(tái)tomcat?catalina?log出現(xiàn)亂碼問題

    本文介紹了如何通過設(shè)置Tomcat和IDEA的編碼格式來解決編碼問題,首先嘗試修改Tomcat的logging.properties文件中的編碼設(shè)置,如果未解決問題,則調(diào)整IDEA的編碼設(shè)置,通過修改vmoptions文件來全局設(shè)置IDEA的編碼格式,作者分享了個(gè)人成功解決問題的方法和步驟,供其他開發(fā)者參考
    2024-09-09
  • SpringCloud的網(wǎng)關(guān)Zuul和Gateway詳解

    SpringCloud的網(wǎng)關(guān)Zuul和Gateway詳解

    SpringCloudZuul和SpringCloudGateway都是用于構(gòu)建微服務(wù)架構(gòu)中的API網(wǎng)關(guān)的組件,但SpringCloudGateway在性能、功能特性和生態(tài)支持等方面有一些優(yōu)勢(shì),因此推薦使用SpringCloudGateway作為首選
    2025-02-02
  • mybatis-plus實(shí)現(xiàn)打印完整sql語句

    mybatis-plus實(shí)現(xiàn)打印完整sql語句

    這篇文章主要介紹了mybatis-plus實(shí)現(xiàn)打印完整sql語句方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Spring Security+Spring Data Jpa如何進(jìn)行安全管理

    Spring Security+Spring Data Jpa如何進(jìn)行安全管理

    這篇文章主要介紹了Spring Security+Spring Data Jpa如何進(jìn)行安全管理,幫助大家更好的理解和學(xué)習(xí)Spring Security框架,感興趣的朋友可以了解下
    2020-09-09
  • 詳解如何在Java中實(shí)現(xiàn)堆排序算法

    詳解如何在Java中實(shí)現(xiàn)堆排序算法

    這篇文章主要為大家詳細(xì)介紹了如何利用Java實(shí)現(xiàn)堆排序算法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 快速入手IntelliJ IDEA基本配置

    快速入手IntelliJ IDEA基本配置

    IntelliJ IDEA是java編程語言開發(fā)的集成環(huán)境,本篇主要介紹了對(duì)它的安裝、配置maven倉庫、調(diào)試方法、常用的插件推薦、快捷鍵大全與常用快捷鍵說明,感興趣的朋友一起看看吧
    2021-10-10

最新評(píng)論

南昌市| 区。| 沙河市| 青岛市| 枝江市| 双峰县| 涪陵区| 琼海市| 平度市| 白山市| 英超| 墨竹工卡县| 崇信县| 施甸县| 恩平市| 建瓯市| 巨鹿县| 彭州市| 宿迁市| 金昌市| 分宜县| 太保市| 思南县| 梅河口市| 东乡| 定安县| 无极县| 高安市| 高雄市| 句容市| 内江市| 井陉县| 孟州市| 合阳县| 包头市| 长垣县| 毕节市| 容城县| 云霄县| 潍坊市| 新丰县|