SpringBoot工程啟動(dòng)時(shí)自動(dòng)執(zhí)行任務(wù)實(shí)現(xiàn)方式
在 Spring Boot 中實(shí)現(xiàn)工程啟動(dòng)時(shí)自動(dòng)執(zhí)行任務(wù)(如開(kāi)始消費(fèi) MQ 數(shù)據(jù))有多種可靠的方式。
以下是幾種常用的方法:
1.使用CommandLineRunner或ApplicationRunner接口
2.使用@PostConstruct注解
3.使用ApplicationListener監(jiān)聽(tīng)ApplicationReadyEvent事件
4.使用@EventListener注解監(jiān)聽(tīng)?wèi)?yīng)用上下文事件
其中,推薦使用ApplicationRunner或CommandLineRunner,或者監(jiān)聽(tīng)ApplicationReadyEvent事件,因?yàn)榇藭r(shí)應(yīng)用上下文已經(jīng)完全準(zhǔn)備好,避免在應(yīng)用還未完全初始化時(shí)就執(zhí)行任務(wù)。
推薦實(shí)現(xiàn)方案
- 使用 ApplicationRunner或 CommandLineRunner(最常用)
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class MqConsumerStarter implements ApplicationRunner {
private final MqConsumerService mqConsumerService;
public MqConsumerStarter(MqConsumerService mqConsumerService) {
this.mqConsumerService = mqConsumerService;
}
@Override
public void run(ApplicationArguments args) throws Exception {
// 應(yīng)用啟動(dòng)后立即執(zhí)行
mqConsumerService.startConsuming();
}
}
特點(diǎn)??:
- 在 ApplicationContext完全加載后執(zhí)行
- 可以訪問(wèn)所有 Spring Bean
- 支持多個(gè) Runner 并指定執(zhí)行順序
- 使用 @EventListener監(jiān)聽(tīng) ApplicationReadyEvent
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MqConsumerInitializer {
private final MqConsumerService mqConsumerService;
public MqConsumerInitializer(MqConsumerService mqConsumerService) {
this.mqConsumerService = mqConsumerService;
}
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReady() {
// 應(yīng)用完全啟動(dòng)后執(zhí)行
mqConsumerService.startConsuming();
}
}
特點(diǎn)??:
- 在應(yīng)用完全就緒后執(zhí)行(包括所有 Runner 執(zhí)行完畢)
- 確保所有 Bean 已初始化完成
- 避免在上下文未完全準(zhǔn)備好時(shí)操作
- 使用 SmartLifecycle接口(適合長(zhǎng)期運(yùn)行任務(wù))
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;
@Component
public class MqConsumerLifecycle implements SmartLifecycle {
private final MqConsumerService mqConsumerService;
private volatile boolean running = false;
public MqConsumerLifecycle(MqConsumerService mqConsumerService) {
this.mqConsumerService = mqConsumerService;
}
@Override
public void start() {
if (!running) {
mqConsumerService.startConsuming();
running = true;
}
}
@Override
public void stop() {
if (running) {
mqConsumerService.stopConsuming();
running = false;
}
}
@Override
public boolean isRunning() {
return running;
}
@Override
public int getPhase() {
return Integer.MAX_VALUE; // 最后啟動(dòng)
}
}
特點(diǎn)??:
- 支持啟動(dòng)/停止生命周期管理
- 可以控制啟動(dòng)順序(通過(guò) getPhase())
- 適合需要優(yōu)雅關(guān)閉的資源
MQ 消費(fèi)實(shí)現(xiàn)示例(RabbitMQ)
- 配置 RabbitMQ 監(jiān)聽(tīng)器
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class RabbitMqConsumer {
@RabbitListener(queues = "${mq.queue.name}")
public void handleMessage(String message) {
System.out.println("Received message: " + message);
// 處理消息邏輯
}
}
- 啟動(dòng)時(shí)自動(dòng)創(chuàng)建隊(duì)列和綁定
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMqConfig {
@Bean
public Queue myQueue() {
return new Queue("${mq.queue.name}", true); // 持久化隊(duì)列
}
}
- 手動(dòng)控制消費(fèi)啟停
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.stereotype.Service;
@Service
public class MqConsumerService {
private final SimpleMessageListenerContainer container;
public MqConsumerService(SimpleMessageListenerContainer container) {
this.container = container;
}
public void startConsuming() {
if (!container.isRunning()) {
container.start();
}
}
public void stopConsuming() {
if (container.isRunning()) {
container.stop();
}
}
}
Kafka 消費(fèi)實(shí)現(xiàn)示例
- 配置 Kafka 監(jiān)聽(tīng)器
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
@Component
public class KafkaConsumer {
@KafkaListener(topics = "${kafka.topic.name}", groupId = "${kafka.group.id}")
public void listen(String message) {
System.out.println("Received Message: " + message);
// 處理消息邏輯
}
}
- 手動(dòng)控制消費(fèi)啟停
import org.springframework.kafka.config.KafkaListenerEndpointRegistry;
import org.springframework.stereotype.Service;
@Service
public class KafkaConsumerService {
private final KafkaListenerEndpointRegistry registry;
public KafkaConsumerService(KafkaListenerEndpointRegistry registry) {
this.registry = registry;
}
public void startConsuming() {
registry.getListenerContainers().forEach(container -> {
if (!container.isRunning()) {
container.start();
}
});
}
public void stopConsuming() {
registry.getListenerContainers().forEach(container -> {
if (container.isRunning()) {
container.stop();
}
});
}
}
最佳實(shí)踐與注意事項(xiàng)
- 啟動(dòng)順序控制
// 使用 @Order 控制多個(gè) Runner 的執(zhí)行順序
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class FirstRunner implements ApplicationRunner {
// ...
}
@Component
@Order(Ordered.LOWEST_PRECEDENCE)
public class LastRunner implements ApplicationRunner {
// ...
}
- 錯(cuò)誤處理與重試機(jī)制
@Override
public void run(ApplicationArguments args) {
try {
mqConsumerService.startConsuming();
} catch (Exception e) {
// 添加重試邏輯
int maxRetries = 5;
for (int i = 0; i < maxRetries; i++) {
try {
Thread.sleep(5000); // 等待5秒重試
mqConsumerService.startConsuming();
break;
} catch (Exception ex) {
logger.error("Retry {} failed: {}", i+1, ex.getMessage());
}
}
}
}
- 優(yōu)雅關(guān)閉
import javax.annotation.PreDestroy;
@Component
public class MqConsumerLifecycle {
@PreDestroy
public void onShutdown() {
// 應(yīng)用關(guān)閉時(shí)停止消費(fèi)
mqConsumerService.stopConsuming();
}
}
常見(jiàn)問(wèn)題解決方案
- 依賴未初始化問(wèn)題
// 使用 @DependsOn 確保依賴順序
@Component
@DependsOn("mqConnectionFactory")
public class MqConsumerStarter implements ApplicationRunner {
// ...
}
- 配置加載問(wèn)題
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReady() {
// 確保所有配置已加載
}
- 多環(huán)境控制
@Profile("!test") // 不在測(cè)試環(huán)境啟用
@Component
public class ProductionMqConsumer implements ApplicationRunner {
// ...
}
- 并發(fā)啟動(dòng)問(wèn)題
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
@Async("taskExecutor")
@Override
public void run(ApplicationArguments args) {
// 單線程順序執(zhí)行
}
總結(jié)
在 Spring Boot 中實(shí)現(xiàn)啟動(dòng)時(shí)自動(dòng)執(zhí)行任務(wù)的最佳實(shí)踐:
1.?? 推薦使用??:
- ApplicationRunner或 CommandLineRunner:簡(jiǎn)單直接
- @EventListener(ApplicationReadyEvent.class):確保完全就緒
2.?? 復(fù)雜場(chǎng)景??:
- SmartLifecycle:需要生命周期管理
- @PostConstruct+ @Async:異步執(zhí)行
3.?? 關(guān)鍵注意事項(xiàng)??:
- 確保依賴已初始化
- 添加錯(cuò)誤處理和重試機(jī)制
- 實(shí)現(xiàn)優(yōu)雅關(guān)閉
- 集成健康檢查
- 多環(huán)境配置控制
4.?? MQ 消費(fèi)最佳實(shí)踐??:
- 使用 Spring 原生支持(如 @RabbitListener)
- 配置連接池和重試機(jī)制
- 監(jiān)控消費(fèi)狀態(tài)和性能
通過(guò)以上方法,可以可靠的在 Spring Boot 應(yīng)用啟動(dòng)時(shí)自動(dòng)執(zhí)行 MQ 消費(fèi)等初始化任務(wù),確保系統(tǒng)穩(wěn)定運(yùn)行。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot啟動(dòng)后自動(dòng)執(zhí)行方法的各種方式對(duì)比
- SpringBoot啟動(dòng)時(shí)自動(dòng)執(zhí)行特定代碼的完整指南
- SpringBoot啟動(dòng)后自動(dòng)執(zhí)行初始化任務(wù)的五種方法
- SpringBoot啟動(dòng)時(shí)自動(dòng)執(zhí)行指定方法的幾種實(shí)現(xiàn)方式
- SpringBoot啟動(dòng)時(shí)自動(dòng)執(zhí)行代碼的幾種實(shí)現(xiàn)方式
- springboot 項(xiàng)目容器啟動(dòng)后如何自動(dòng)執(zhí)行指定方法
- SpringBoot啟動(dòng)時(shí)自動(dòng)執(zhí)行sql腳本的方法步驟
- springBoot啟動(dòng)時(shí)讓方法自動(dòng)執(zhí)行的幾種實(shí)現(xiàn)方式
- SpringBoot 啟動(dòng)時(shí)自動(dòng)執(zhí)行代碼的幾種方式講解
相關(guān)文章
Java中數(shù)組的使用與注意事項(xiàng)詳解(推薦)
數(shù)組是一組地址連續(xù)、長(zhǎng)度固定的具有相同類型的數(shù)據(jù)的集合,通過(guò)數(shù)組下標(biāo)我們可以指定數(shù)字中的每一個(gè)元素,下面這篇文章主要給大家介紹了關(guān)于Java中數(shù)組的使用與注意事項(xiàng)的相關(guān)資料,需要的朋友可以參考下2021-08-08
Java數(shù)據(jù)結(jié)構(gòu)與算法之雙向鏈表、環(huán)形鏈表及約瑟夫問(wèn)題深入理解
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)與算法之雙向鏈表、環(huán)形鏈表及約瑟夫問(wèn)題深入理解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
Spring?Security權(quán)限注解啟動(dòng)及邏輯處理使用示例
這篇文章主要為大家介紹了Spring?Security權(quán)限注解啟動(dòng)及邏輯處理使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
SpringBoot+actuator和admin-UI實(shí)現(xiàn)監(jiān)控中心方式
這篇文章主要介紹了SpringBoot+actuator和admin-UI實(shí)現(xiàn)監(jiān)控中心方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
SpringMVC框架如何與Junit整合看這個(gè)就夠了
這篇文章主要介紹了SpringMVC框架如何與Junit整合看這個(gè)就夠了,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05

