一:执行上下文
1.1 Job Context 作业上下文
JobContext 绑定 JobExecution 执行对象,为Job作业执行提供执行环境(上下文)。
1.2 Step Context 步骤上下文
StepContext 绑定 StepExecution 执行对象,为Step步骤执行提供执行环境(上下文)。
1.3 ExecutionContext 执行上下文
- Job ExecutionContext:一次作业运行,所有Step步骤间数据共享。
- Step ExecutionContext:一次步骤运行,单个Step步骤间(ItemReader/ItemProcessor/ItemWrite组件间)数据共享。
1.4 作业线
-
作业线:
Job---JobInstance---JobContext---JobExecution--ExecutionContext
-
步骤线:
Step--StepContext --StepExecution--ExecutionContext
1.5 作业上下文API JobSynchronizationManager
JobContext context = JobSynchronizationManager.getContext();
JobExecution jobExecution = context.getJobExecution();
Map<String, Object> jobExecutionContext = context.getJobExecutionContext();
Map<String, Object> jobParameters1 = context.getJobParameters();
1.6 步骤上下文API
StepContext stepContext = chunkContext.getStepContext();
StepExecution stepExecution = stepContext.getStepExecution();
Map<String, Object> stepExecutionContext = stepContext.getStepExecutionContext();
Map<String, Object> jobExecutionContext = stepContext.getJobExecutionContext();
ExecutionContext executionContext = stepExecution.getExecutionContext();
executionContext.put("key", "value");
JobExecution jobExecution = stepExecution.getJobExecution();
ExecutionContext executionContext1 = jobExecution.getExecutionContext();
executionContext1.put("key2", "value2");
1.7上下文案例
@Bean
@StepScope
public Tasklet hellWorldTasklet(@Value("#{jobParameters['timestamp']}") Long timestamp) {
return new Tasklet() {
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
// 步骤上下文
ExecutionContext stepExeCtx = chunkContext.getStepContext().getStepExecution().getExecutionContext();
stepExeCtx.put("step-context-key1", "value1");
// 作业上下文
ExecutionContext jobExeCtx = chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext();
jobExeCtx.put("job-context-key1", "value1");
return RepeatStatus.FINISHED;
}
};
}
@Bean
public Tasklet hellWorldTasklet2() {
return new Tasklet() {
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
// 步骤上下文(步骤上下文在步骤里面相当于是局部变量,在其它Step中获取不到)
ExecutionContext stepExeCtx = chunkContext.getStepContext().getStepExecution().getExecutionContext();
// null(作业上下文不是用来作业之间共享数据的,而是用来ItemReader、ItemProcessor、ItemWriter之间共享数据的,所以取不到)
Object stepCtxValue = stepExeCtx.get("step-context-key1");
// 作业上下文(用来步骤之间共享数据的,所以步骤2能获取到步骤1的数据)
ExecutionContext jobExeCtx = chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext();
// value1
Object JobCtxValue = jobExeCtx.get("job-context-key1");
return RepeatStatus.FINISHED;
}
};
}
BATCH_JOB_EXECUTION_CONTEXT
BATCH_STEP_EXECUTION_CONTEXT
二:单步骤重启继续运行
该示例是模拟的当一个步骤发生错误时,当修复好错误时,会接上次错误处继续执行,而不是从第一个步骤完重新开始。
@Configuration
public class ErrorJobConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Job errorJob() {
return jobBuilderFactory.get("errorJob")
.start(step1())
.next(step2())
.build();
}
@Bean
public Step step1() {
return stepBuilderFactory.get("step1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
ExecutionContext executionContext = chunkContext.getStepContext().getStepExecution().getExecutionContext();
if (!executionContext.containsKey("mock")) {
executionContext.put("mock", "value");
System.err.println("step1模拟发生异常");
throw new RuntimeException("step1模拟发生异常");
}
System.err.println("step1 execute finish");
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step step2() {
return stepBuilderFactory.get("step2")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
ExecutionContext executionContext = chunkContext.getStepContext().getStepExecution().getExecutionContext();
if (!executionContext.containsKey("mock")) {
executionContext.put("mock", "value");
System.err.println("step2模拟发生异常");
throw new RuntimeException("step2模拟发生异常");
}
System.err.println("step2 execute finish");
return RepeatStatus.FINISHED;
}
})
.build();
}
}
@RequestMapping("/start")
public ExitStatus start() throws Exception {
JobExecution jobExecution = jobLauncher.run(errorJob, new JobParameters());
return jobExecution.getExitStatus();
}
第一次执行进入step1中的if,抛出异常。BATCH_STEP_EXECUTION_CONTEXT表存储mock键值。
第二次执行,因为第一次执行已经存储了键值对,所以step1不抛异常,正常执行结束。但是step2会进入if,模拟抛出异常。
第三次执行,可以看到step1并没有重复执行,而是跳过成功执行的step1,从错误开始的位置step2开始执行。