Skip to content
微信扫码关注公众号

Spring Batch 执行上下文(Execution Context)

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

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