Skip to content

Spring Batch 执行上下文(Execution Context)

🏷️ Spring Batch

批处理过程在本质上是有状态的。

JobExecution 表示的是 Job 的一次尝试运行,作业的状态在 ExecutionContext (执行上下文)中维护。

每个 JobExecution 有一个对应的 ExecutionContext 。每个 JobExecution 可能对应多个 StepExecution ,而每个 StepExecution 也各有一个对应的 ExecutionContext

其关系可以参考 Spring Batch 数据库的 ER 图:

ExecutionContext 提供了一种“安全”的方式来存储数据。存储是安全的,这是因为进入 ExecutionContext 的任何内容都会被持久化到作业存储库中。

操作 ExecutionContext

Spring Batch 提供了三种方式操作执行上下文:

  1. 通过从块( Chunk )到步骤( Step )再到作业( Job )的方式获取作业的执行上下文;

    java
    ExecutionContext 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);
  2. 通过执行上下文提升监听器( 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();
    }
  3. 通过 ItemStream 实例;

    后面才会讨论,暂未看到。

运行后会在 DB 中可以看到类似如下数据:

  • BATCH_JOB_EXECUTION_CONTEXT 表的 SHORT_CONTEXT 字段

    可以看到 step1step2 中添加的数据均保存在了作业的执行上下文数据中。

    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.taskletTypebatch.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)著