iT邦幫忙

2023 iThome 鐵人賽

DAY 17
0
Software Development

救救我啊我救我!CRUD 工程師的惡補日記系列 第 17

【Spring Boot】使用 Command Line Runner 在啟動後執行動作

  • 分享至 

  • xImage
  •  

今天要介紹的是「CommandLineRunner」與「ApplicationRunner」,它們可以在 Spring Boot 應用程式啟動完成後,自動執行一些程式。在筆者進行了解後,發覺這是相對單純的功能。因此文末會分享一些拙見,提出可能可以在什麼情況下使用。


一、建立 CommandLineRunner

我們可以很容易地建立一個 CommandLineRunner。只要建立一個元件類別,並實作 CommandLineRunner 介面即可。

@Component
public class MyRunner implements CommandLineRunner {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void run(String... args) throws Exception {
        logger.info("{} works!", getClass().getSimpleName());
    }
}

實作 CommandLineRunner 介面,需要完成它的 run 方法。在此只是簡單地印出 log 文字。啟動後,便能在 console 看到 log。

由於是在 Spring Boot 啟動完成後,才會執行這個 runner,因此我們自己開發的其它元件(bean),都可注入進來使用。

另外,關於 run 方法的參數 args,指的是啟動 JAR 檔所附加的參數選項。例如:

java -jar XXX.jar --spring.config.name=application --spring.config.location=./

二、例外處理

在使用 CommandLineRunner 時要注意,如果中途發生了 exception,卻沒有做例外處理,那麼 Spring Boot 就會啟動失敗,程式中止。

以下是一個會故意拋出例外的 runner。

@Component
public class MyRunner implements CommandLineRunner {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void run(String... args) throws Exception {
        // 計數 1 ~ 5
        for (var i = 1; i <= 5; i++) {
            logger.info("{} counts: {}", getClass().getSimpleName(), i);

            // 數到 4 時拋出例外
            if (i == 4) {
                throw new RuntimeException("MyRunner fails...");
            }
        }
    }
}

下圖是以上的 runner 執行到一半,發生例外而導致程式中止的 log。
https://ithelp.ithome.com.tw/upload/images/20230920/201311079JjnZEp9N4.jpg

所以讀者使用 CommandLineRunner 時,請評估是否有發生例外的可能性。如果有,而且有方案去因應這種情況,別忘了使用 try catch 將程式邏輯包起來,並做處理。

三、安排執行順序

我們可能會基於不同的目的,實作出多個 CommandLineRunner。若想要求這些 runner 按照指定的順序去執行,則可使用 @Order 標記。

@Component
@Order(2)
public class MyRunnerA implements CommandLineRunner {
    // ...
}

@Component
@Order(1)
public class MyRunnerB implements CommandLineRunner {
    // ...
}

這個 @Order 標記可傳入一個數字參數,代表執行順序。Spring Boot 會讓數字小的先執行。

如果不使用此標記,將會依照類別名稱遞增的順序來執行,讀者可自行嘗試。

四、ApplicationRunner

另外還有一個叫做「ApplicationRunner」的元件,可以達到與 CommandLineRunner 一樣的效果。程式的實作方式也雷同。

我們在第一節有提到,啟動 JAR 檔時可以附加一些參數選項。CommandLineRunner 與 ApplicationRunner 的差別,就是 run 方法包裝這些參數選項的方式不同罷了。

@Component
public class MyRunnerC implements ApplicationRunner {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.info("--- Non option args ---");
        args.getNonOptionArgs().forEach(logger::info);

        logger.info("--- Option values ---");
        args.getOptionNames().forEach(name -> {
            List<String> values = args.getOptionValues(name);
            logger.info("{}: {}", name, String.join(",", values));
        });
    }
}

CommandLineRunner 是以 String 陣列包裝參數;而 ApplicationRunner 則是 ApplicationArguments。前者比較 raw data,而後者能夠有系統地取出裡面的資料。

假設啟動 JAR 檔的指令如下。

java -jar XXX.jar --name=Vincent --msg=hello --msg=world ithome ironman

則以上 ApplicationRunner 印出的 log 內容如下。
https://ithelp.ithome.com.tw/upload/images/20230920/20131107etCE82C1Jj.jpg

這些選項參數並沒有規定只能輸入什麼,所以我們能附加任何自己想要的參數,傳遞給 ApplicationRunner 或 CommandLineRunner。

五、可以使用的場合

最後談談這些 runner 可以用在什麼時機。由於筆者見識尚淺,所以能提出的想法並不多。

(一)Server 啟動通知

在 Day 12 的文章,筆者有實作出寄 email 的功能。搭配 runner,可以在 server 成功啟動後,立即發送訊息給相關人員。或者有些公司會串接 Slack 等通訊軟體,也能在 server 啟動後通知大家。

(二)將資料放到全文檢索引擎

筆者前公司的產品,有整合全文檢索引擎的搜尋功能,意即對資料的一至多個欄位做關鍵字查詢。為此,公司會把 DB 的資料傳送到 Elasticsearch 這款全文檢索引擎做保存。

而查詢 DB 時,若聯合太多表做 JOIN,會有查詢費時的問題。因此將某些業務所需要的 JOIN 好的資料,事先放在 Elasticsearch,或許是不錯的選擇。

在 server 啟動後,可以透過 runner 幫忙做這些事。

(三)初始化資料

筆者前公司的系統,會將各個幣別的匯率放在 HashMap 中作為快取。然而 server 重新啟動後,這些資料理所當然會消失。

在 Day 16 的文章,筆者有實作出從第三方 API 取得匯率的程式。在 server 啟動後,可以透過 runner 開始取得匯率,並將資料放入快取,完成初始化的工作。

Ref:
Spring Boot 2 (七):Spring Boot 如何解決項目啟動時初始化資源
SpringBoot - 實現啟動時執行指定任務(CommandLineRunner、ApplicationRunner)


今日文章到此結束!
最後推廣一下自己的部落格,我是「新手工程師的程式教室」的作者,請多指教/images/emoticon/emoticon41.gif


上一篇
【Spring Boot】RestTemplate 串接第三方服務實例
下一篇
【RabbitMQ】認識訊息佇列並導入到 Spring Boot
系列文
救救我啊我救我!CRUD 工程師的惡補日記50
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言