1. 概述
本文将深入探讨并对比在不同业务场景下,触发和停止定时 Spring Batch 任务的几种常见方式。
如果你对 Spring Batch 或定时任务(Scheduler)还不熟悉,建议先阅读 Spring Batch 入门 和 Spring 定时任务 相关文章。
2. 触发一个定时的 Spring Batch 任务
我们首先定义一个名为 SpringBatchScheduler
的类,用于配置定时任务和批处理作业。其中 launchJob()
方法将被注册为一个定时执行的任务。
最直观的触发方式是:✅ 通过一个条件开关控制任务是否执行。这样可以在不关闭调度器的前提下灵活启停任务。
示例代码如下:
private AtomicBoolean enabled = new AtomicBoolean(true);
private AtomicInteger batchRunCounter = new AtomicInteger(0);
@Autowired
private JobLauncher jobLauncher;
@Autowired
private JobRepository jobRepository;
@Autowired
private PlatformTransactionManager transactionManager;
@Scheduled(fixedRate = 2000)
public void launchJob() throws Exception {
Date date = new Date();
logger.debug("scheduler starts at " + date);
if (enabled.get()) {
JobExecution jobExecution = jobLauncher.run(job(jobRepository, transactionManager), new JobParametersBuilder().addDate("launchDate", date)
.toJobParameters());
batchRunCounter.incrementAndGet();
logger.debug("Batch job ends with status as " + jobExecution.getStatus());
}
logger.debug("scheduler ends ");
}
📌 说明:
enabled
是一个原子布尔值,作为任务执行的“开关”。batchRunCounter
用于记录任务实际执行次数,方便在集成测试中验证任务是否被正确停止。- 使用
@Scheduled(fixedRate = 2000)
实现每 2 秒触发一次。
这种方式的优点是:简单粗暴,且支持后续动态恢复,适合需要临时暂停但保留调度器的场景。
3. 停止一个定时的 Spring Batch 任务
上面的方式只是“跳过执行”,调度任务本身仍在运行。如果我们确定不再需要该任务,可以考虑彻底停止调度器,以节省资源。
以下是两种彻底停止调度任务的方式。
3.1. 使用 Scheduler 后置处理器(ScheduledAnnotationBeanPostProcessor)
Spring 在处理 @Scheduled
注解时,会自动注册一个 ScheduledAnnotationBeanPostProcessor
,它负责解析注解并创建定时任务。
我们可以通过手动调用其 postProcessBeforeDestruction()
方法,来销毁指定的调度 Bean,从而停止任务。
@Test
public void stopJobSchedulerWhenSchedulerDestroyed() throws Exception {
ScheduledAnnotationBeanPostProcessor bean = context
.getBean(ScheduledAnnotationBeanPostProcessor.class);
SpringBatchScheduler schedulerBean = context
.getBean(SpringBatchScheduler.class);
await().untilAsserted(() -> Assert.assertEquals(
2,
schedulerBean.getBatchRunCounter().get()));
bean.postProcessBeforeDestruction(
schedulerBean, "SpringBatchScheduler");
await().atLeast(3, SECONDS);
Assert.assertEquals(
2,
schedulerBean.getBatchRunCounter().get());
}
⚠️ 注意:
- 这种方式会直接销毁调度 Bean,不可恢复。
- 推荐将每个调度器独立成一个类,便于精准控制。
✅ 适用场景:
- 多个调度器共存时,需要单独关闭某一个。
- 应用运行期间明确知道某个任务不再需要。
3.2. 取消调度任务的 ScheduledFuture
另一种更精细的控制方式是:捕获任务的 ScheduledFuture
对象,并在需要时主动取消。
我们可以自定义一个 TaskScheduler
,在调度任务时记录 Future
引用:
@Bean
public TaskScheduler poolScheduler() {
return new CustomTaskScheduler();
}
private class CustomTaskScheduler
extends ThreadPoolTaskScheduler {
private final Map<Object, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();
@Override
public ScheduledFuture<?> scheduleAtFixedRate(
Runnable task, long period) {
ScheduledFuture<?> future = super
.scheduleAtFixedRate(task, period);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
scheduledTasks.put(runnable.getTarget(), future);
return future;
}
}
然后提供一个方法,用于取消特定调度器的任务:
public void cancelFutureSchedulerTasks() {
scheduledTasks.forEach((k, v) -> {
if (k instanceof SpringBatchScheduler) {
v.cancel(false);
}
});
}
📌 要点:
scheduledTasks
是一个ConcurrentHashMap
,键为调度目标对象(如SpringBatchScheduler
实例),值为对应的ScheduledFuture
。v.cancel(false)
表示不中断正在运行的任务,但阻止后续执行。
✅ 优势:
- 精准控制,支持按类、按实例取消。
- 可扩展性强,适合复杂调度场景。
4. 总结
本文介绍了三种控制定时 Spring Batch 任务的方式:
方式 | 是否可恢复 | 适用场景 |
---|---|---|
✅ 条件开关(enabled 标志) | 是 | 临时暂停,后续可能恢复 |
✅ 销毁调度 Bean(postProcessBeforeDestruction) | 否 | 永久关闭,节省资源 |
✅ 取消 ScheduledFuture | 否(但更可控) | 需要精细控制取消行为 |
📌 建议:
- 如果只是想“暂停”任务,用 条件开关 最简单。
- 如果确认任务不再需要,推荐 取消 Future 或 销毁 Bean,避免资源浪费。
所有示例代码已上传至 GitHub:https://github.com/example/spring-batch-scheduling-demo(原仓库为 eugenp/tutorials,此处为模拟地址)