命令行与 Web 启动
本章定位:掌握 Spring Batch Job 的三种启动方式——CommandLineRunner、REST API 和定时调度,以及生产环境的启动最佳实践。
定义与作用
Spring Batch Job 定义好后,需要通过某种方式触发执行。常见的三种触发方式对应不同的使用场景。
飞翔科技后端开发小崔的总结:
"命令行适合运维手动触发和 Cron 脚本,REST API 适合管理后台点击触发,@Scheduled 适合无人值守的定时任务。但不管哪种方式,最终都调用
jobLauncher.run(job, params)。"
核心原理
三种方式对比
| 方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 命令行 | 运维脚本、一次性任务 | 简单直接,不依赖 Web 容器 | 需要服务器权限 |
| REST API | 管理后台、手动触发 | 灵活,可集成到任何前端 | 需要 Web 容器 |
| @Scheduled | 定时批处理 | 无人值守,自动运行 | 不支持集群(需额外处理) |
完整示例
场景一:命令行启动
@SpringBootApplication
public class BatchApplication implements CommandLineRunner {
private final JobLauncher jobLauncher;
private final Job orderJob;
public BatchApplication(JobLauncher jobLauncher,
@Qualifier("orderProcessingJob") Job orderJob) {
this.jobLauncher = jobLauncher;
this.orderJob = orderJob;
}
public static void main(String[] args) {
SpringApplication.run(BatchApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
// 读取命令行参数
String date = args.length > 0 ? args[0] : LocalDate.now().toString();
JobParameters params = new JobParametersBuilder()
.addString("orderDate", date)
.addLong("run.id", System.currentTimeMillis())
.toJobParameters();
JobExecution execution = jobLauncher.run(orderJob, params);
System.out.println("Exit Status: " + execution.getExitStatus().getExitCode());
}
}
# 命令行执行
java -jar feixiang-batch.jar 2026-06-13
# Exit Status: COMPLETED
场景二:REST API 触发(异步)
@RestController
@RequestMapping("/api/batch")
public class AsyncBatchController {
private final JobLauncher asyncJobLauncher;
private final Job orderJob;
public AsyncBatchController(
@Qualifier("asyncJobLauncher") JobLauncher asyncJobLauncher,
@Qualifier("orderProcessingJob") Job orderJob) {
this.asyncJobLauncher = asyncJobLauncher;
this.orderJob = orderJob;
}
@PostMapping("/orders/run")
public ResponseEntity<Map<String, Object>> runOrderJob(
@RequestParam String date) throws Exception {
JobParameters params = new JobParametersBuilder()
.addString("orderDate", date)
.addLong("triggeredAt", System.currentTimeMillis())
.toJobParameters();
// 异步启动:立即返回 executionId
JobExecution execution = asyncJobLauncher.run(orderJob, params);
return ResponseEntity.accepted().body(Map.of(
"executionId", execution.getId(),
"status", "STARTED",
"checkUrl", "/api/batch/orders/status/" + execution.getId()
));
}
@GetMapping("/orders/status/{id}")
public ResponseEntity<Map<String, Object>> getStatus(
@PathVariable Long id, JobExplorer explorer) {
JobExecution exec = explorer.getJobExecution(id);
return ResponseEntity.ok(Map.of(
"executionId", exec.getId(),
"status", exec.getStatus().name(),
"startTime", exec.getStartTime(),
"endTime", exec.getEndTime()
));
}
}
场景三:定时调度 + 防止 Spring Boot 自动启动
# application.properties
spring.batch.job.enabled=false # 禁止 Spring Boot 启动时自动执行所有 Job
@Component
public class ScheduledBatchJobs {
private final JobLauncher jobLauncher;
private final Job reportJob;
public ScheduledBatchJobs(JobLauncher jobLauncher,
@Qualifier("nightlyReportJob") Job reportJob) {
this.jobLauncher = jobLauncher;
this.reportJob = reportJob;
}
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨 2:00
public void runNightlyReport() throws Exception {
JobParameters params = new JobParametersBuilder()
.addString("reportDate", LocalDate.now().toString())
.addLong("run.id", System.currentTimeMillis())
.toJobParameters();
jobLauncher.run(reportJob, params);
}
}
易错场景与避坑
反例一:忘记设置 spring.batch.job.enabled=false
# ❌ 没有这个配置 → Spring Boot 启动时自动执行所有 Job
# spring.batch.job.enabled 默认为 true
后果:应用启动时就跑批处理,导致启动极慢。如果应用是 Web 服务,用户请求会被阻塞。
反例二:同步 Web 请求 + 长时间 Job
// ❌ 同步 JobLauncher + HTTP 请求 → 请求超时
JobExecution exec = syncJobLauncher.run(longJob, params); // 跑 30 分钟
// HTTP 请求 30 秒就超时了
正确做法:Web 触发必须使用异步 JobLauncher。
面试高频考点
Q1:spring.batch.job.enabled=false 的作用?
禁止 Spring Boot 在启动时自动执行所有已注册的 Job Bean。在 Web 应用中必须设置为 false,否则应用启动时会自动跑批处理。
Q2:如何实现集群环境下的定时 Job 去重?
@Scheduled 在每个实例上都执行,会导致重复。解决方案:①使用分布式锁(Redis/ZK);②使用 Spring Cloud Data Flow 的调度器;③外部 Cron + 单实例执行脚本。
上一章:Chunk 处理模型详解下一章:监听器详解