JobParameters 详解
本章定位:彻底理解 JobParameters 的设计意图——不仅是"启动参数",更是 JobInstance 的唯一性标识和延迟绑定的数据源。
定义与作用
JobParameters 是启动 Job 时传入的键值对参数集合,具有双重作用:
- 标识 JobInstance:同一 Job + 同一组 JobParameters → 同一个 JobInstance
- 传递运行时数据:如文件路径、处理日期、批次号等
飞翔科技架构师白歌强调:
"JobParameters 和普通方法参数最大的不同在于:它是不可变的、被持久化到元数据表的、参与 JobInstance 标识的。你今天用
date=2026-06-13跑一遍,明天用相同的参数再跑就会报错——因为 Spring Batch 认为这是同一个 JobInstance。"
核心原理
JobParameters 的生命周期
参数类型与识别
JobParameters 支持四种类型,通过 identifying 标志区分:
| 参数类型 | 方法 | identifying 默认值 | 说明 |
|---|---|---|---|
| String | addString(key, value) | true | 最常用,如文件路径、日期字符串 |
| Long | addLong(key, value) | true | 如 run.id、时间戳 |
| Double | addDouble(key, value) | true | 如金额阈值 |
| Date | addDate(key, value) | true | 如处理截止日期 |
Identifying vs Non-Identifying:
- Identifying 参数(默认):参与 JobInstance 识别。参数变了 → 新的 JobInstance
- Non-Identifying 参数:不参与识别。参数变了仍然复用同一个 JobInstance
// 两种参数混合使用
JobParameters params = new JobParametersBuilder()
.addString("input.file", "students.csv") // identifying(默认)
.addLong("run.id", 1L) // identifying
.addString("email.to", "ops@feixiang.net", false) // non-identifying
.addLong("batch.size", 500L, false) // non-identifying
.toJobParameters();
完整示例
场景一:飞翔科技——按日期处理订单文件
背景:运营部杨英每天收到格式为 orders_YYYYMMDD.csv 的订单文件,需要用 Spring Batch 处理。
@RestController
public class OrderJobController {
private final JobLauncher jobLauncher;
private final Job orderJob;
public OrderJobController(JobLauncher jobLauncher, Job orderJob) {
this.jobLauncher = jobLauncher;
this.orderJob = orderJob;
}
@PostMapping("/jobs/orders")
public String runOrderJob(@RequestParam String date) throws Exception {
JobParameters params = new JobParametersBuilder()
.addString("orderDate", date) // 如 "2026-06-13"
.addString("input.file", "orders_" + date + ".csv") // 动态文件路径
.addLong("run.id", System.currentTimeMillis()) // 确保唯一性
.toJobParameters();
JobExecution execution = jobLauncher.run(orderJob, params);
return "Job started: " + execution.getJobId();
}
}
操作前后对比:
POST /jobs/orders?date=2026-06-13
→ JobParameters: {orderDate=2026-06-13, input.file=orders_20260613.csv, run.id=1718265600000}
→ JobInstance #1, JobExecution #1 → 正常执行
POST /jobs/orders?date=2026-06-13 (重复请求)
→ JobParameters: {orderDate=2026-06-13, input.file=orders_20260613.csv, run.id=1718265600001}
→ JobInstance #2 ← run.id 不同,所以是新实例!
POST /jobs/orders?date=2026-06-14
→ JobParameters: {orderDate=2026-06-14, input.file=orders_20260614.csv, run.id=1718352000000}
→ JobInstance #3 ← orderDate 也不同了
场景二:延迟绑定——在 Reader 中使用 JobParameters
@StepScope + @Value 是访问 JobParameters 最常用的方式:
@Bean
@StepScope
public FlatFileItemReader<Order> orderReader(
@Value("#{jobParameters['input.file']}") String inputFile) {
return new FlatFileItemReaderBuilder<Order>()
.name("orderReader")
.resource(new FileSystemResource(inputFile)) // 运行时才确定文件路径
.delimited().delimiter(",")
.names("orderId", "customerName", "amount", "orderDate")
.targetType(Order.class)
.linesToSkip(1)
.build();
}
关键:@StepScope 确保 Bean 在 Step 启动时才实例化,此时 JobParameters 已经可用。如果不加 @StepScope,Spring 在容器初始化时就尝试解析 #{jobParameters[...]},此时参数还未传入。
易错场景与避坑
反例一:忘记加 @StepScope 导致启动报错
// ❌ 没有 @StepScope → Spring 初始化时就尝试解析 SpEL → 报错
@Bean
public FlatFileItemReader<Order> reader(
@Value("#{jobParameters['input.file']}") String inputFile) {
// ...
}
// 错误:EL1007E: Property or field 'input' cannot be found on null
正确做法:必须加 @StepScope。
反例二:Non-Identifying 参数误解
小崔以为只要参数标记为 non-identifying 就不会影响 JobInstance,但实际行为更微妙:
// 第一次执行
JobParameters params1 = new JobParametersBuilder()
.addString("date", "2026-06-13")
.addLong("batch.size", 100L, false) // non-identifying
.toJobParameters();
jobLauncher.run(job, params1); // → JobInstance #1,COMPLETED
// 第二次执行——只改了 non-identifying 参数
JobParameters params2 = new JobParametersBuilder()
.addString("date", "2026-06-13") // 相同
.addLong("batch.size", 500L, false) // 不同,但 non-identifying
.toJobParameters();
jobLauncher.run(job, params2);
// → JobInstanceAlreadyCompleteException!
// 因为 identifying 参数相同 → 同一个 JobInstance → 已 COMPLETED
正确理解:Non-identifying 参数不参与 JobInstance 识别,但也不足以区分不同的 JobInstance。需要 identifying 参数变化才能创建新的 JobInstance。
面试高频考点
Q1:JobParameters 和 ExecutionContext 有什么区别?
JobParameters 是启动时传入的不可变参数,用于标识 JobInstance。ExecutionContext 是运行时的可读写状态存储,用于断点续传和步骤间数据传递。前者是"输入",后者是"中间状态"。
Q2:Identifying 和 Non-Identifying 参数的区别?
Identifying 参数参与 JobInstance 的唯一性识别(默认行为),Non-Identifying 参数不参与。如果两次执行的 Identifying 参数完全相同 → 同一个 JobInstance。Non-Identifying 参数通常用于传递不影响实例唯一性的配置值(如邮件接收人、批处理大小)。
Q3:如何保证每次启动都创建新的 JobInstance?
三种方式:① 使用
RunIdIncrementer(最推荐);② 在参数中加入时间戳System.currentTimeMillis();③ 在参数中加入业务唯一标识(如日期 + 序列号)。
上一章:Job 配置下一章:JobLauncher 详解