SpringBoot工程啟動時自動執(zhí)行任務實現(xiàn)方式
在 Spring Boot 中實現(xiàn)工程啟動時自動執(zhí)行任務(如開始消費 MQ 數(shù)據(jù))有多種可靠的方式。
以下是幾種常用的方法:
1.使用CommandLineRunner或ApplicationRunner接口
2.使用@PostConstruct注解
3.使用ApplicationListener監(jiān)聽ApplicationReadyEvent事件
4.使用@EventListener注解監(jiān)聽應用上下文事件
其中,推薦使用ApplicationRunner或CommandLineRunner,或者監(jiān)聽ApplicationReadyEvent事件,因為此時應用上下文已經(jīng)完全準備好,避免在應用還未完全初始化時就執(zhí)行任務。
推薦實現(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 {
// 應用啟動后立即執(zhí)行
mqConsumerService.startConsuming();
}
}
特點??:
- 在 ApplicationContext完全加載后執(zhí)行
- 可以訪問所有 Spring Bean
- 支持多個 Runner 并指定執(zhí)行順序
- 使用 @EventListener監(jiān)聽 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() {
// 應用完全啟動后執(zhí)行
mqConsumerService.startConsuming();
}
}
特點??:
- 在應用完全就緒后執(zhí)行(包括所有 Runner 執(zhí)行完畢)
- 確保所有 Bean 已初始化完成
- 避免在上下文未完全準備好時操作
- 使用 SmartLifecycle接口(適合長期運行任務)
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; // 最后啟動
}
}
特點??:
- 支持啟動/停止生命周期管理
- 可以控制啟動順序(通過 getPhase())
- 適合需要優(yōu)雅關閉的資源
MQ 消費實現(xiàn)示例(RabbitMQ)
- 配置 RabbitMQ 監(jiān)聽器
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);
// 處理消息邏輯
}
}
- 啟動時自動創(chuàng)建隊列和綁定
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); // 持久化隊列
}
}
- 手動控制消費啟停
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 消費實現(xiàn)示例
- 配置 Kafka 監(jiān)聽器
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);
// 處理消息邏輯
}
}
- 手動控制消費啟停
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();
}
});
}
}
最佳實踐與注意事項
- 啟動順序控制
// 使用 @Order 控制多個 Runner 的執(zhí)行順序
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class FirstRunner implements ApplicationRunner {
// ...
}
@Component
@Order(Ordered.LOWEST_PRECEDENCE)
public class LastRunner implements ApplicationRunner {
// ...
}
- 錯誤處理與重試機制
@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)雅關閉
import javax.annotation.PreDestroy;
@Component
public class MqConsumerLifecycle {
@PreDestroy
public void onShutdown() {
// 應用關閉時停止消費
mqConsumerService.stopConsuming();
}
}
常見問題解決方案
- 依賴未初始化問題
// 使用 @DependsOn 確保依賴順序
@Component
@DependsOn("mqConnectionFactory")
public class MqConsumerStarter implements ApplicationRunner {
// ...
}
- 配置加載問題
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReady() {
// 確保所有配置已加載
}
- 多環(huán)境控制
@Profile("!test") // 不在測試環(huán)境啟用
@Component
public class ProductionMqConsumer implements ApplicationRunner {
// ...
}
- 并發(fā)啟動問題
@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í)行
}
總結
在 Spring Boot 中實現(xiàn)啟動時自動執(zhí)行任務的最佳實踐:
1.?? 推薦使用??:
- ApplicationRunner或 CommandLineRunner:簡單直接
- @EventListener(ApplicationReadyEvent.class):確保完全就緒
2.?? 復雜場景??:
- SmartLifecycle:需要生命周期管理
- @PostConstruct+ @Async:異步執(zhí)行
3.?? 關鍵注意事項??:
- 確保依賴已初始化
- 添加錯誤處理和重試機制
- 實現(xiàn)優(yōu)雅關閉
- 集成健康檢查
- 多環(huán)境配置控制
4.?? MQ 消費最佳實踐??:
- 使用 Spring 原生支持(如 @RabbitListener)
- 配置連接池和重試機制
- 監(jiān)控消費狀態(tài)和性能
通過以上方法,可以可靠的在 Spring Boot 應用啟動時自動執(zhí)行 MQ 消費等初始化任務,確保系統(tǒng)穩(wěn)定運行。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java中ReentrantLock和ReentrantReadWriteLock的原理
這篇文章主要介紹了Java中ReentrantLock和ReentrantReadWriteLock的原理,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-09-09
spring boot與redis 實現(xiàn)session共享教程
這篇文章主要介紹了spring boot與redis 實現(xiàn)session共享教程,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-04-04
Springboot如何通過yml配置文件為靜態(tài)成員變量賦值
這篇文章主要介紹了Springboot如何通過yml配置文件為靜態(tài)成員變量賦值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
java調用FFmpeg實現(xiàn)視屏壓縮功能的詳細步驟
這篇文章主要介紹了java調用FFmpeg實現(xiàn)視屏壓縮功能,本文簡單的展示了java調用FFmpeg命令實現(xiàn)視屏的壓縮的詳細步驟,需要的朋友可以參考下2021-09-09
java 數(shù)據(jù)結構二叉樹的實現(xiàn)代碼
這篇文章主要介紹了java 數(shù)據(jù)結構二叉樹的實現(xiàn)代碼的相關資料,需要的朋友可以參考下2016-09-09

