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)文章
詳解Spring boot Admin 使用eureka監(jiān)控服務(wù)
本篇文章主要介紹了詳解Spring boot Admin 使用eureka監(jiān)控服務(wù),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
Struts2源碼分析之ParametersInterceptor攔截器
這篇文章主要介紹了Struts2源碼分析之ParametersInterceptor攔截器,ParametersInterceptor攔截器其主要功能是把ActionContext中的請(qǐng)求參數(shù)設(shè)置到ValueStack中,,需要的朋友可以參考下2019-06-06
基于JVM 調(diào)優(yōu)的技巧總結(jié)分析
本篇文章是對(duì)JVM 調(diào)優(yōu)的技巧進(jìn)行了總結(jié)和分析。需要的朋友參考下2013-05-05
spring security實(shí)現(xiàn)下次自動(dòng)登錄功能過(guò)程解析
這篇文章主要介紹了spring security實(shí)現(xiàn)記住我下次自動(dòng)登錄功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11

