一文詳解SpringBoot中CommandLineRunner接口
前言
Spring Boot的CommandLineRunner接口是一個(gè)函數(shù)式接口,用于在Spring Boot應(yīng)用程序啟動(dòng)后執(zhí)行一些初始化操作。它提供了一個(gè)run方法,該方法在應(yīng)用程序啟動(dòng)后被調(diào)用。
使用CommandLineRunner接口,可以在應(yīng)用程序啟動(dò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)建了一個(gè)名為MyCommandLineRunner的類,并實(shí)現(xiàn)了CommandLineRunner接口。在run方法中,我們可以編寫需要在應(yīng)用程序啟動(dòng)后執(zhí)行的初始化邏輯。
需要注意的是,實(shí)現(xiàn)CommandLineRunner接口的類需要被Spring容器掃描到,可以使用@Component注解或其他方式將其注冊(cè)為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)建了一個(gè) DataInitializer 類,實(shí)現(xiàn) CommandLineRunner 接口。在 run() 方法中,我們注入了 UserRepository,然后創(chuàng)建了兩個(gè)用戶對(duì)象保存到數(shù)據(jù)庫中。這個(gè)類會(huì)在 Spring Boot 應(yīng)用啟動(dòng)完成后執(zhí)行,從而實(shí)現(xiàn)了數(shù)據(jù)預(yù)加載的效果。通過 CommandLineRunner,我們可以靈活地在 Spring Boot 啟動(dòng)時(shí)進(jìn)行一些初始化操作,如預(yù)先加載測(cè)試數(shù)據(jù)、插入管理員賬戶等,很好地增強(qiáng)了應(yīng)用的功能。
假設(shè)我們有一個(gè)User模型和用戶Repository,需要在Spring Boot啟動(dòng)時(shí)預(yù)加載幾個(gè)用戶數(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)建幾個(gè)用戶
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接口,然后注入U(xiǎn)serRepository bean。在run方法中,首先清空所有數(shù)據(jù),然后創(chuàng)建兩個(gè)用戶對(duì)象并保存,最后打印已保存的用戶數(shù)。這樣在Spring Boot應(yīng)用啟動(dòng)完成后,就會(huì)自動(dòng)執(zhí)行run方法,預(yù)加載指定的用戶數(shù)據(jù)。
啟動(dòng)后打印應(yīng)用信息
可以打印出一些應(yīng)用啟動(dòng)信息,如啟動(dò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("========= 打印啟動(dòng)信息 =========");
// 打印應(yīng)用端口
log.info(("端口號(hào): " + 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("========= 打印啟動(dòng)信息結(jié)束 =========");
}
}
執(zhí)行打印結(jié)果

啟動(dòng)異步任務(wù)
可以使用多線程啟動(dòng)一些異步任務(wù),進(jìn)行后臺(tái)數(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啟動(dòng)。
示例
@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("消息隊(duì)列服務(wù)不可用,退出應(yīng)用!");
System.exit(1);
}
log.info("所有服務(wù)正常,應(yīng)用啟動(dòng)。");
}
}
這里我們注入兩個(gè)依賴服務(wù) DatabaseService 和 MessageQueueService。在run方法中,調(diào)用它們的健康檢查方法,如果任何一個(gè)服務(wù)不可用,則直接調(diào)用System.exit(1)退出Spring Boot應(yīng)用啟動(dòng)。
外部服務(wù)調(diào)用
可以在啟動(dòng)時(shí)調(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請(qǐng)求進(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)
可以對(duì)輸入的運(yùn)行參數(shù)做校驗(yàn),如果不滿足條件可以終止Spring Boot啟動(dòng)。
示例
@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ù)不正確,請(qǐng)傳入至少2個(gè)參數(shù)!");
System.exit(1);
}
// 校驗(yàn)參數(shù)2是否為數(shù)字
if(!args[1].matches("\\d+")) {
log.error("第二個(gè)參數(shù)必須是數(shù)字!");
System.exit(1);
}
// 校驗(yàn)通過,應(yīng)用繼續(xù)啟動(dòng)
log.info("參數(shù)校驗(yàn)通過,應(yīng)用啟動(dòng)中...");
}
}
在run方法中,我們可以對(duì)main方法輸入的參數(shù)args進(jìn)行自定義校驗(yàn):
檢查參數(shù)數(shù)量校驗(yàn)參數(shù)類型
如果參數(shù)不滿足需求,可以直接調(diào)用System.exit(1)來終止Spring Boot的啟動(dòng)。這樣就可以在應(yīng)用啟動(dòng)前驗(yàn)證參數(shù)的正確性,避免應(yīng)用啟動(dòng)后發(fā)生未知錯(cuò)誤。
動(dòng)態(tài)設(shè)置配置
可以根據(jù)運(yùn)行參數(shù)等條件動(dòng)態(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);
// 動(dòng)態(tài)設(shè)置配置
myConfig.setFoo("new value");
myConfig.setBar(100);
// 打印更新后的配置
System.out.println("Updated config: " + myConfig);
}
}
啟動(dòng)阻塞
可以使應(yīng)用啟動(dò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),阻塞啟動(dòng)流程
waitAuth();
// 授權(quán)完成后繼續(xù)啟動(dòng)
log.info("管理員已授權(quán),應(yīng)用啟動(dòng)中...");
}
private void waitAuth() {
// 死循環(huán)模擬等待管理員操作授權(quán)
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
}
}
總結(jié)
通過 CommandLineRunner,我們可以深度控制 Spring Boot 應(yīng)用的啟動(dòng)流程,在應(yīng)用啟動(dòng)階段增強(qiáng)各種自定義邏輯。是 Spring Boot 提供的一個(gè)很實(shí)用的擴(kuò)展點(diǎn)。
以上就是一文詳解SpringBoot中CommandLineRunner接口的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot CommandLineRunner接口的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot多數(shù)據(jù)源讀寫分離的自定義配置問題及解決方法
這篇文章主要介紹了SpringBoot多數(shù)據(jù)源讀寫分離的自定義配置,我們可以通過自定義配置數(shù)據(jù)庫配置類來解決這個(gè)問題,方式有很多,不同的業(yè)務(wù)采用的方式也不同,下面我簡(jiǎn)單的介紹我們項(xiàng)目的使用的方法2022-06-06
解決FeignClient發(fā)送post請(qǐng)求異常的問題
這篇文章主要介紹了FeignClient發(fā)送post請(qǐng)求異常的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
SpringBoot Actuator未授權(quán)訪問漏洞解決方案
工作的時(shí)候遇到過提示Spring Boot后端存在Actuator未授權(quán)訪問漏洞,網(wǎng)上有很多詳細(xì)的解釋文章,在這里做一個(gè)簡(jiǎn)單的總結(jié)、介紹和分享,需要的朋友可以參考下2023-09-09
java根據(jù)網(wǎng)絡(luò)地址保存圖片的方法
這篇文章主要為大家詳細(xì)介紹了java根據(jù)網(wǎng)絡(luò)地址保存圖片的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
面向?qū)ο蠛兔嫦蜻^程的區(qū)別(動(dòng)力節(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),針對(duì)裝修樣板信息管理混亂,出錯(cuò)率高,信息安全性差,勞動(dòng)強(qiáng)度大,費(fèi)時(shí)費(fèi)力等問題開發(fā)了這套系統(tǒng),需要的朋友可以參考下2023-03-03
springboot?項(xiàng)目啟動(dòng)后無日志輸出直接結(jié)束的解決
這篇文章主要介紹了springboot?項(xiàng)目啟動(dòng)后無日志輸出直接結(jié)束的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
70行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ì)算過程,神經(jīng)網(wǎng)絡(luò)的算法程序?qū)崿F(xiàn),多層神經(jīng)網(wǎng)絡(luò)完整程序?qū)崿F(xiàn)等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以參考下。2017-11-11
關(guān)于springboot中對(duì)sqlSessionFactoryBean的自定義
這篇文章主要介紹了springboot中對(duì)sqlSessionFactoryBean的自定義方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12

