Spring Boot 工程启动时自动执行任务方法

在 Spring Boot 中实现工程启动时自动执行任务(如开始消费 MQ 数据)有多种可靠的方式。以下是几种常用的方法:

1.使用CommandLineRunner或ApplicationRunner接口

2.使用@PostConstruct注解

3.使用ApplicationListener监听ApplicationReadyEvent事件

4.使用@EventListener注解监听应用上下文事件

其中,推荐使用ApplicationRunner或CommandLineRunner,或者监听ApplicationReadyEvent事件,因为此时应用上下文已经完全准备好,避免在应用还未完全初始化时就执行任务。

推荐实现方案

  1. 使用 ApplicationRunner或 CommandLineRunner(最常用)
java 复制代码
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 {
        // 应用启动后立即执行
        mqConsumerService.startConsuming();
    }
}

特点​​:

• 在 ApplicationContext完全加载后执行

• 可以访问所有 Spring Bean

• 支持多个 Runner 并指定执行顺序

  1. 使用 @EventListener监听 ApplicationReadyEvent
java 复制代码
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() {
        // 应用完全启动后执行
        mqConsumerService.startConsuming();
    }
}

特点​​:

• 在应用完全就绪后执行(包括所有 Runner 执行完毕)

• 确保所有 Bean 已初始化完成

• 避免在上下文未完全准备好时操作

  1. 使用 SmartLifecycle接口(适合长期运行任务)
java 复制代码
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())

• 适合需要优雅关闭的资源

MQ 消费实现示例(RabbitMQ)

  1. 配置 RabbitMQ 监听器
java 复制代码
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);
        // 处理消息逻辑
    }
}
  1. 启动时自动创建队列和绑定
java 复制代码
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); // 持久化队列
    }
}
  1. 手动控制消费启停
java 复制代码
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 消费实现示例

  1. 配置 Kafka 监听器
java 复制代码
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);
        // 处理消息逻辑
    }
}
  1. 手动控制消费启停
java 复制代码
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();
            }
        });
    }
}

最佳实践与注意事项

  1. 启动顺序控制
java 复制代码
// 使用 @Order 控制多个 Runner 的执行顺序
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class FirstRunner implements ApplicationRunner {
    // ...
}

@Component
@Order(Ordered.LOWEST_PRECEDENCE)
public class LastRunner implements ApplicationRunner {
    // ...
}
  1. 错误处理与重试机制
java 复制代码
@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());
            }
        }
    }
}
  1. 优雅关闭
java 复制代码
import javax.annotation.PreDestroy;

@Component
public class MqConsumerLifecycle {

    @PreDestroy
    public void onShutdown() {
        // 应用关闭时停止消费
        mqConsumerService.stopConsuming();
    }
}

常见问题解决方案

  1. 依赖未初始化问题
java 复制代码
// 使用 @DependsOn 确保依赖顺序
@Component
@DependsOn("mqConnectionFactory")
public class MqConsumerStarter implements ApplicationRunner {
    // ...
}
  1. 配置加载问题
java 复制代码
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReady() {
    // 确保所有配置已加载
}
  1. 多环境控制
java 复制代码
@Profile("!test") // 不在测试环境启用
@Component
public class ProductionMqConsumer implements ApplicationRunner {
    // ...
}
  1. 并发启动问题
java 复制代码
@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) {
    // 单线程顺序执行
}

总结

在 Spring Boot 中实现启动时自动执行任务的最佳实践:

1.​​ 推荐使用​​:

• ApplicationRunner或 CommandLineRunner:简单直接

• @EventListener(ApplicationReadyEvent.class):确保完全就绪

2.​​ 复杂场景​​:

• SmartLifecycle:需要生命周期管理

• @PostConstruct+ @Async:异步执行

3.​​ 关键注意事项​​:

• 确保依赖已初始化

• 添加错误处理和重试机制

• 实现优雅关闭

• 集成健康检查

• 多环境配置控制

4.​​ MQ 消费最佳实践​​:

• 使用 Spring 原生支持(如 @RabbitListener)

• 配置连接池和重试机制

• 监控消费状态和性能

通过以上方法,可以可靠的在 Spring Boot 应用启动时自动执行 MQ 消费等初始化任务,确保系统稳定运行。

相关推荐
独断万古他化几秒前
【Java 实战项目】多用户网页版聊天室:消息传输模块 —— 基于 WebSocket 实现实时通信
java·spring boot·后端·websocket·ajax·mybatis
舒一笑5 分钟前
🚀 我用一行命令,把 OSS 私有文件变成“可直接下载的公网链接”(很多人不会)
后端
yyt3630458417 分钟前
spring单例bean线程安全问题讨论
java·spring
小兔崽子去哪了17 分钟前
Docker 安装 PostgreSQL
数据库·后端·postgresql
Sweet锦18 分钟前
SpringBoot 3.5 集成 InfluxDB 1.8
spring boot·时序数据库
野犬寒鸦21 分钟前
Redis热点key问题解析与实战解决方案(附大厂实际方案讲解)
服务器·数据库·redis·后端·缓存·bootstrap
我是大猴子27 分钟前
事务失效的几种情况以及是为什么(详解)
java·开发语言
snakeshe10101 小时前
深入理解 Java 注解:从原理到实战
后端
Lucaju1 小时前
吃透 Spring AI Alibaba 多智能体|四大协同模式+完整代码
后端
Nyarlathotep01131 小时前
Redis的对象(5):有序集合对象
redis·后端