一文詳解SpringBoot中CommandLineRunner接口
前言
Spring Boot的CommandLineRunner接口是一個函數(shù)式接口,用于在Spring Boot應(yīng)用程序啟動后執(zhí)行一些初始化操作。它提供了一個run方法,該方法在應(yīng)用程序啟動后被調(diào)用。
使用CommandLineRunner接口,可以在應(yīng)用程序啟動后執(zhí)行一些必要的初始化操作,例如加載配置文件、初始化數(shù)據(jù)庫連接、創(chuàng)建默認(rèn)數(shù)據(jù)等。可以通過實(shí)現(xiàn)CommandLineRunner接口,并重寫run方法來定義自己的初始化邏輯。
實(shí)例
導(dǎo)入庫
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.0</version> </parent> <groupId>org.example</groupId> <artifactId>springboot-CommandLineRunner</artifactId> <version>1.0-SNAPSHOT</version> <name>Spring Boot banner</name> <description>Spring Boot and commandLineRunner</description> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
application.yaml
server: port: 8080 spring: profiles: active: dev
Runner
import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component @Slf4j public class Runner implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("The Runner start to initialize ..."); } }
SpringBootCommandLineRunnerApplication
import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @Slf4j public class SpringBootCommandLineRunnerApplication { public static void main(String[] args) { SpringApplication.run(SpringBootCommandLineRunnerApplication.class, args); log.info("The service to end"); } }
執(zhí)行結(jié)果
在上面的示例中,我們創(chuàng)建了一個名為MyCommandLineRunner的類,并實(shí)現(xiàn)了CommandLineRunner接口。在run方法中,我們可以編寫需要在應(yīng)用程序啟動后執(zhí)行的初始化邏輯。
需要注意的是,實(shí)現(xiàn)CommandLineRunner接口的類需要被Spring容器掃描到,可以使用@Component注解或其他方式將其注冊為Spring Bean。
先后順序示例
可以通過@Order()來設(shè)置Runner的先后順序,在上面例子的基礎(chǔ)上增加
OrderRunner1
import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Component @Order(1) @Slf4j public class OrderRunner1 implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("The OrderRunner1 start to initialize ..."); } }
OrderRunner2
import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Component @Order(2) @Slf4j public class OrderRunner2 implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("The OrderRunner2 start to initialize ..."); } }
執(zhí)行結(jié)果
通常用法
加載初始化數(shù)據(jù)
可以實(shí)現(xiàn)CommandLineRunner接口,在run方法中加載一些初始化數(shù)據(jù)到數(shù)據(jù)庫等。適合做一些數(shù)據(jù)預(yù)加載工作。
示例
@Component public class DataInitializer implements CommandLineRunner { @Autowired private UserRepository userRepository; @Override public void run(String... args) throws Exception { // 創(chuàng)建初始用戶 User admin = new User("admin", "123456"); userRepository.save(admin); User normalUser = new User("user", "123456"); userRepository.save(normalUser); System.out.println("數(shù)據(jù)加載完畢!"); } }
這里創(chuàng)建了一個 DataInitializer 類,實(shí)現(xiàn) CommandLineRunner 接口。在 run() 方法中,我們注入了 UserRepository,然后創(chuàng)建了兩個用戶對象保存到數(shù)據(jù)庫中。這個類會在 Spring Boot 應(yīng)用啟動完成后執(zhí)行,從而實(shí)現(xiàn)了數(shù)據(jù)預(yù)加載的效果。通過 CommandLineRunner,我們可以靈活地在 Spring Boot 啟動時進(jìn)行一些初始化操作,如預(yù)先加載測試數(shù)據(jù)、插入管理員賬戶等,很好地增強(qiáng)了應(yīng)用的功能。
假設(shè)我們有一個User模型和用戶Repository,需要在Spring Boot啟動時預(yù)加載幾個用戶數(shù)據(jù),可以這樣使用CommandLineRunner:
@Component public class DataInitializer implements CommandLineRunner { @Autowired private UserRepository userRepository; @Override public void run(String... args) throws Exception { // 清除所有數(shù)據(jù) userRepository.deleteAll(); // 創(chuàng)建幾個用戶 User user1 = new User("John", "john@example.com"); User user2 = new User("Mary", "mary@example.com"); userRepository.save(user1); userRepository.save(user2); // 打印已保存用戶數(shù) System.out.println("Number of users saved: " + userRepository.count()); } }
這里我們實(shí)現(xiàn)了CommandLineRunner接口,然后注入UserRepository bean。在run方法中,首先清空所有數(shù)據(jù),然后創(chuàng)建兩個用戶對象并保存,最后打印已保存的用戶數(shù)。這樣在Spring Boot應(yīng)用啟動完成后,就會自動執(zhí)行run方法,預(yù)加載指定的用戶數(shù)據(jù)。
啟動后打印應(yīng)用信息
可以打印出一些應(yīng)用啟動信息,如啟動端口、運(yùn)行環(huán)境信息等,用于確認(rèn)應(yīng)用配置。
示例
@Component @Slf4j public class AppInfoPrinter implements CommandLineRunner { @Autowired private Environment environment; @Override public void run(String... args) throws Exception { log.info("========= 打印啟動信息 ========="); // 打印應(yīng)用端口 log.info(("端口號: " + environment.getProperty("server.port"))); // 打印當(dāng)前環(huán)境 log.info("當(dāng)前環(huán)境: " + environment.getProperty("spring.profiles.active")); // 打印JDK版本 log.info("JDK 版本: " + System.getProperty("java.version")); log.info("========= 打印啟動信息結(jié)束 ========="); } }
執(zhí)行打印結(jié)果
啟動異步任務(wù)
可以使用多線程啟動一些異步任務(wù),進(jìn)行后臺數(shù)據(jù)處理等復(fù)雜業(yè)務(wù)邏輯。
示例
@Component @Slf4j public class AsyncTaskRunner implements CommandLineRunner { @Autowired private AsyncTaskService asyncTaskService; @Override public void run(String... args) throws Exception { log.info("========= 執(zhí)行任務(wù) ========="); // 在新線程中執(zhí)行任務(wù) new Thread(() -> { asyncTaskService.doTaskOne(); asyncTaskService.doTaskTwo(); asyncTaskService.doTaskThree(); }).start(); } } @Service @Slf4j class AsyncTaskService { public void doTaskOne() { log.info("執(zhí)行任務(wù)1"); } public void doTaskTwo() { log.info("執(zhí)行任務(wù)2"); } public void doTaskThree() { log.info("執(zhí)行任務(wù)3"); } }
執(zhí)行結(jié)果
[ main] org.example.runner.AsyncTaskRunner : ========= 執(zhí)行任務(wù) ========= [ Thread-1] org.example.runner.AsyncTaskService : 執(zhí)行任務(wù)1 [ Thread-1] org.example.runner.AsyncTaskService : 執(zhí)行任務(wù)2 [ Thread-1] org.example.runner.AsyncTaskService : 執(zhí)行任務(wù)3
接口健康檢查
可以調(diào)用并驗(yàn)證依賴服務(wù)的健康狀態(tài),如果不正??梢越K止Spring Boot啟動。
示例
@Component @Slf4j public class HealthCheckRunner implements CommandLineRunner { @Autowired private DatabaseService databaseService; @Autowired private MessageQueueService messageQueueService; @Override public void run(String... args) throws Exception { if(!databaseService.isConnected()) { log.error("數(shù)據(jù)庫服務(wù)不可用,退出應(yīng)用!"); System.exit(1); } if(!messageQueueService.isConnected()) { log.error("消息隊列服務(wù)不可用,退出應(yīng)用!"); System.exit(1); } log.info("所有服務(wù)正常,應(yīng)用啟動。"); } }
這里我們注入兩個依賴服務(wù) DatabaseService 和 MessageQueueService。在run方法中,調(diào)用它們的健康檢查方法,如果任何一個服務(wù)不可用,則直接調(diào)用System.exit(1)退出Spring Boot應(yīng)用啟動。
外部服務(wù)調(diào)用
可以在啟動時調(diào)用外部服務(wù),進(jìn)行驗(yàn)證、數(shù)據(jù)同步等操作。
示例
@Component public class OtherServiceCheckRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { // 健康檢查的URL String healthCheckUrl = "http://localhost:8080/actuator/health"; RestTemplate restTemplate = new RestTemplate(); // 發(fā)送GET請求進(jìn)行健康檢查 String response = restTemplate.getForObject(healthCheckUrl, String.class); // 根據(jù)響應(yīng)判斷健康狀態(tài) if (response.contains("\"status\":\"UP\"")) { System.out.println("Application is healthy"); } else { System.out.println("Application is not healthy"); } } }
參數(shù)校驗(yàn)
可以對輸入的運(yùn)行參數(shù)做校驗(yàn),如果不滿足條件可以終止Spring Boot啟動。
示例
@Component @Slf4j public class ParameterValidator implements CommandLineRunner { @Override public void run(String... args) throws Exception { // 校驗(yàn)參數(shù)1 if(args.length < 2) { log.error("參數(shù)不正確,請傳入至少2個參數(shù)!"); System.exit(1); } // 校驗(yàn)參數(shù)2是否為數(shù)字 if(!args[1].matches("\\d+")) { log.error("第二個參數(shù)必須是數(shù)字!"); System.exit(1); } // 校驗(yàn)通過,應(yīng)用繼續(xù)啟動 log.info("參數(shù)校驗(yàn)通過,應(yīng)用啟動中..."); } }
在run方法中,我們可以對main方法輸入的參數(shù)args進(jìn)行自定義校驗(yàn):
檢查參數(shù)數(shù)量校驗(yàn)參數(shù)類型
如果參數(shù)不滿足需求,可以直接調(diào)用System.exit(1)來終止Spring Boot的啟動。這樣就可以在應(yīng)用啟動前驗(yàn)證參數(shù)的正確性,避免應(yīng)用啟動后發(fā)生未知錯誤。
動態(tài)設(shè)置配置
可以根據(jù)運(yùn)行參數(shù)等條件動態(tài)設(shè)置Spring Boot的配置,實(shí)現(xiàn)不同環(huán)境的適配。
示例 application.yaml
myconfig: foo: 十五 bar: 1
MyConfig
@Component @Data @ConfigurationProperties(prefix = "myconfig") public class MyConfig { private String foo; private int bar; // getter和setter方法省略 @Override public String toString() { return "MyConfig{" + "foo='" + foo + '\'' + ", bar=" + bar + '}'; } }
ConfigRunner
@Component @EnableConfigurationProperties(MyConfig.class) public class ConfigRunner implements CommandLineRunner { @Autowired private MyConfig myConfig; @Override public void run(String... args) throws Exception { // 打印當(dāng)前配置 System.out.println("Current config: " + myConfig); // 動態(tài)設(shè)置配置 myConfig.setFoo("new value"); myConfig.setBar(100); // 打印更新后的配置 System.out.println("Updated config: " + myConfig); } }
啟動阻塞
可以使應(yīng)用啟動后阻塞住主線程,防止main方法直接退出,從而保持Spring Boot應(yīng)用運(yùn)行。
示例
@Component @Slf4j public class StartBlocker implements CommandLineRunner { @Override public void run(String... args) throws Exception { // 加載提示信息 log.info("正在等待管理員授權(quán)..."); // 等待授權(quán),阻塞啟動流程 waitAuth(); // 授權(quán)完成后繼續(xù)啟動 log.info("管理員已授權(quán),應(yīng)用啟動中..."); } private void waitAuth() { // 死循環(huán)模擬等待管理員操作授權(quán) while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { break; } } } }
總結(jié)
通過 CommandLineRunner,我們可以深度控制 Spring Boot 應(yīng)用的啟動流程,在應(yīng)用啟動階段增強(qiáng)各種自定義邏輯。是 Spring Boot 提供的一個很實(shí)用的擴(kuò)展點(diǎn)。
以上就是一文詳解SpringBoot中CommandLineRunner接口的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot CommandLineRunner接口的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot多數(shù)據(jù)源讀寫分離的自定義配置問題及解決方法
這篇文章主要介紹了SpringBoot多數(shù)據(jù)源讀寫分離的自定義配置,我們可以通過自定義配置數(shù)據(jù)庫配置類來解決這個問題,方式有很多,不同的業(yè)務(wù)采用的方式也不同,下面我簡單的介紹我們項目的使用的方法2022-06-06解決FeignClient發(fā)送post請求異常的問題
這篇文章主要介紹了FeignClient發(fā)送post請求異常的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07SpringBoot Actuator未授權(quán)訪問漏洞解決方案
工作的時候遇到過提示Spring Boot后端存在Actuator未授權(quán)訪問漏洞,網(wǎng)上有很多詳細(xì)的解釋文章,在這里做一個簡單的總結(jié)、介紹和分享,需要的朋友可以參考下2023-09-09java根據(jù)網(wǎng)絡(luò)地址保存圖片的方法
這篇文章主要為大家詳細(xì)介紹了java根據(jù)網(wǎng)絡(luò)地址保存圖片的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07面向?qū)ο蠛兔嫦蜻^程的區(qū)別(動力節(jié)點(diǎn)java學(xué)院整理)
很多朋友不清楚面向?qū)ο蠛兔嫦蜻^程有什么區(qū)別,接下來小編給大家整理了關(guān)于面向?qū)ο蠛兔嫦蜻^程的區(qū)別講解,感興趣的朋友可以參考下2017-04-04用SpringBoot+Vue+uniapp小程序?qū)崿F(xiàn)在線房屋裝修管理系統(tǒng)
這篇文章主要介紹了用SpringBoot+Vue+uniapp實(shí)現(xiàn)在線房屋裝修管理系統(tǒng),針對裝修樣板信息管理混亂,出錯率高,信息安全性差,勞動強(qiáng)度大,費(fèi)時費(fèi)力等問題開發(fā)了這套系統(tǒng),需要的朋友可以參考下2023-03-03springboot?項目啟動后無日志輸出直接結(jié)束的解決
這篇文章主要介紹了springboot?項目啟動后無日志輸出直接結(jié)束的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-1270行Java代碼實(shí)現(xiàn)深度神經(jīng)網(wǎng)絡(luò)算法分享
這篇文章主要介紹了70行Java代碼實(shí)現(xiàn)深度神經(jīng)網(wǎng)絡(luò)算法分享,涉及神經(jīng)網(wǎng)絡(luò)的計算過程,神經(jīng)網(wǎng)絡(luò)的算法程序?qū)崿F(xiàn),多層神經(jīng)網(wǎng)絡(luò)完整程序?qū)崿F(xiàn)等相關(guān)內(nèi)容,具有一定參考價值,需要的朋友可以參考下。2017-11-11關(guān)于springboot中對sqlSessionFactoryBean的自定義
這篇文章主要介紹了springboot中對sqlSessionFactoryBean的自定義方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12