Spring Batch 执行上下文(Execution Context)
🏷️ Spring Batch
批处理过程在本质上是有状态的。
JobExecution
表示的是 Job 的一次尝试运行,作业的状态在 ExecutionContext
(执行上下文)中维护。
每个 JobExecution
有一个对应的 ExecutionContext
。每个 JobExecution
可能对应多个 StepExecution
,而每个 StepExecution
也各有一个对应的 ExecutionContext
。
其关系可以参考 Spring Batch 数据库的 ER 图:
ExecutionContext
提供了一种“安全”的方式来存储数据。存储是安全的,这是因为进入 ExecutionContext
的任何内容都会被持久化到作业存储库中。
操作 ExecutionContext
Spring Batch 提供了三种方式操作执行上下文:
通过从块( Chunk )到步骤( Step )再到作业( Job )的方式获取作业的执行上下文;
javaExecutionContext jobContext = chunkContext.getStepContext() .getStepExecution() .getJobExecution() .getExecutionContext(); jobContext.put("step1.name", name);
需要注意的是
StepContext
提供了一个getJobExecutionContext()
获取作业的执行上下文,但是这个执行上下文是不可修改的(UnmodifiableMap
)。java// 不会持久化到真正的执行上下文中 Map<String, Object> readonlyJobContext = chunkContext.getStepContext() .getJobExecutionContext(); // getJobExecutionContext() 方法返回的是 UnmodifiableMap // 尝试修改会报 UnsupportedOperationException 异常 // readonlyJobContext.put("step1.readonlyName", name);
通过执行上下文提升监听器(
ExecutionContextPromotionListener
)提升步骤的执行上下文到作业的执行上下文;promotionListener 会在步骤完成后,在步骤的
ExecutionContext
中查找指定的键值。如果查找到,就会复制到作业的执行上下文;如果没找到,那么不会发生任何事情。也可以配置监听器,在没找到时抛出异常。java@Bean("promotionListener") public StepExecutionListener promotionListener() { ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener(); listener.setKeys(new String[]{"step2.name"}); return listener; } @Bean("step2") public Step step2(@Qualifier("helloWorldTasklet") Tasklet helloWorldTasklet, @Qualifier("promotionListener") StepExecutionListener promotionListener) { return this.stepBuilderFactory.get("step2") .tasklet(helloWorldTasklet) .listener(promotionListener) .build(); }
通过
ItemStream
实例;后面才会讨论,暂未看到。
运行后会在 DB 中可以看到类似如下数据:
BATCH_JOB_EXECUTION_CONTEXT 表的 SHORT_CONTEXT 字段
可以看到 step1 和 step2 中添加的数据均保存在了作业的执行上下文数据中。
json{ "@class": "java.util.HashMap", "step1.name": "jiajia", "step2.name": "jiajia" }
BATCH_STEP_EXECUTION_CONTEXT 表的 SHORT_CONTEXT 字段
由于有 3 个步骤,所有生成了 3 条记录。其中,只有 step2 由于操作的是步骤的执行上下文,所有存储的数据中也有这个 Key(
step2.name
)。其它的 Key batch.taskletType 和 batch.stepType 都是 Spring Cloud Data Flow 需要使用的值。
json{ "@class": "java.util.HashMap", "batch.taskletType": "me.liujiajia.batch.HelloWorldJob$$Lambda$467/0x0000000800f176b8", "batch.stepType": "org.springframework.batch.core.step.tasklet.TaskletStep" } { "@class": "java.util.HashMap", "batch.taskletType": "jdk.proxy2.$Proxy56", "step2.name": "jiajia", "batch.stepType": "org.springframework.batch.core.step.tasklet.TaskletStep" } { "@class": "java.util.HashMap", "batch.taskletType": "jdk.proxy2.$Proxy56", "batch.stepType": "org.springframework.batch.core.step.tasklet.TaskletStep" }
参考
[1]:《Spring Batch 权威指南》 -- [美] 迈克尔·T.米内拉(Michael,T.,Minella)著