乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 批处理概述与 Spring Batch 核心理念

    • Spring Batch 概述
    • Job-Instance-Execution 三层生命周期
    • 三层架构
    • Chunk 处理模型
    • Tasklet 处理模型
  • 第2章 Job与作业配置

    • Job 详解
    • Job 配置
    • JobLauncher 详解
    • JobParameters 详解
    • JobRepository 详解
  • 第3章 Step与执行模型

    • Step 详解
    • Step 配置
    • ExecutionContext 详解
    • ItemStream 与状态管理
    • StepScope 与 JobScope
  • 第4章 ItemReader数据读取

    • ItemReader 详解
    • FlatFileItemReader 详解
    • JdbcItemReader 详解
    • MultiResourceItemReader 详解
  • 第5章 ItemProcessor 数据处理

    • ItemProcessor 详解
  • 第6章 ItemWriter 数据写出

    • ItemWriter 详解
    • FlatFileItemWriter 详解
    • CompositeItemWriter 详解
    • JdbcBatchItemWriter 详解
  • 第7章 Chunk 处理与事务边界

    • Chunk 处理模型详解
    • 事务边界
  • 第8章 作业参数与启动

    • 命令行与 Web 启动
  • 第9章 监听器与拦截器

    • 监听器详解
  • 第10章 重启重试与跳过策略

    • 重启与重试
    • 跳过策略
  • 第11章 作业流与条件决策

    • 作业流与条件决策
  • 第12章 分区与并行处理

    • 分区详解
    • 并行 Step 与 Split
  • 第13章 与 Spring Boot 集成实践

    • Spring Boot 集成
  • 第15章 运维与监控

    • 大作业设计模式
    • 运维与监控

ExecutionContext 详解

本章定位:深入理解 ExecutionContext——Spring Batch 的"中间记事本",Step 间数据传递和断点续传的关键机制。

定义与作用

ExecutionContext 是 Spring Batch 中的键值对集合(类似 Map),用于在批处理执行过程中存储和传递状态信息。它分为两个级别:

级别作用域存储位置生命周期
Job ExecutionContext整个 Job 内所有 Step 共享BATCH_JOB_EXECUTION_CONTEXT从 Job 启动到结束
Step ExecutionContext单个 Step 内部BATCH_STEP_EXECUTION_CONTEXT从 Step 启动到结束(重启时恢复)

飞翔科技架构师白歌的比喻:

"Job ExecutionContext 是贴在集装箱上的运单——所有环节都能看到。Step ExecutionContext 是每个工人自己的笔记本——记到第几行了、当前页到哪了,别人不需要知道,但下次接着干时必须能拿出来继续用。"

核心原理

ExecutionContext 的读写和持久化时机

put vs putInt / putLong

ExecutionContext ctx = stepExecution.getExecutionContext();

// ✅ 推荐:类型安全的方法,值不可变
ctx.putInt("current.line", 542);
ctx.putLong("total.processed", 5000L);
ctx.putString("last.processed.id", "STU-00499");

// ⚠️ 谨慎使用:put() 存入 Object,值被序列化后可能丢失类型信息
ctx.put("config", someComplexObject);

完整示例

场景一:飞翔科技——Step 间数据传递

背景:小崔的作业有两个 Step:Step 1 统计无效记录数,Step 2 根据无效记录数决定是否发送告警。

// Step 1: 统计无效记录并存入 Job ExecutionContext
@Component
public class InvalidRecordCounter implements Tasklet {
    @Override
    public RepeatStatus execute(StepContribution contribution,
                                 ChunkContext chunkContext) throws Exception {
        int invalidCount = countInvalidRecords();
        System.out.println("发现无效记录: " + invalidCount + " 条");

        // 存入 Job ExecutionContext(跨 Step 共享)
        chunkContext.getStepContext().getStepExecution()
                .getJobExecution().getExecutionContext()
                .putInt("invalid.count", invalidCount);

        return RepeatStatus.FINISHED;
    }
}

// Step 2: 从 Job ExecutionContext 读取并决策
@Component
public class AlertDecisionTasklet implements Tasklet {
    @Override
    public RepeatStatus execute(StepContribution contribution,
                                 ChunkContext chunkContext) throws Exception {
        // 从 Job ExecutionContext 读取
        ExecutionContext jobCtx = chunkContext.getStepContext()
                .getStepExecution().getJobExecution().getExecutionContext();

        int invalidCount = jobCtx.getInt("invalid.count", 0);
        System.out.println("Job Context 中的无效记录数: " + invalidCount);

        if (invalidCount > 100) {
            System.out.println("发送告警: 无效记录数超过阈值");
            // 发送告警逻辑
        } else {
            System.out.println("无效记录在容忍范围内");
        }
        return RepeatStatus.FINISHED;
    }
}

运行结果:

Step 1 [countStep]: 发现无效记录: 156 条
Step 1 [countStep]: COMPLETED

Step 2 [alertStep]: Job Context 中的无效记录数: 156
Step 2 [alertStep]: 发送告警: 无效记录数超过阈值
Step 2 [alertStep]: COMPLETED

场景二:断点续传——从失败点恢复

@Component
public class ResumableItemReader implements ItemReader<String>, ItemStream {

    private int currentLine = 0;
    private List<String> lines;

    @Override
    public void open(ExecutionContext executionContext) {
        // 重启时从 ExecutionContext 恢复上次位置
        if (executionContext.containsKey("current.line")) {
            currentLine = executionContext.getInt("current.line");
            System.out.println("从第 " + currentLine + " 行恢复");
        } else {
            currentLine = 0;
        }
        lines = Files.readAllLines(Paths.get("data.txt"));
    }

    @Override
    public String read() {
        if (currentLine >= lines.size()) return null;
        return lines.get(currentLine++);
    }

    @Override
    public void update(ExecutionContext executionContext) {
        // 每个 Chunk 提交后,框架调用此方法保存进度
        executionContext.putInt("current.line", currentLine);
    }

    @Override
    public void close() { /* 无需特殊处理 */ }
}

操作前后对比:

场景不使用 ExecutionContext使用 ExecutionContext
处理到第 542 行时崩溃从头重新处理 542 行从第 542 行继续
10 万条数据处理崩溃后全重做仅重做崩溃 Chunk 的 50 条
处理时间O(N) 每次都重跑全量O(N) 仅一次 + 崩溃后补少量

易错场景与避坑

反例一:在 ExecutionContext 中存大对象

// ❌ 存入整个对象列表 → 序列化开销大,可能导致 OOM
ExecutionContext ctx = stepExecution.getExecutionContext();
ctx.put("all.orders", orderList);  // 5 万条 Order 对象!

正确做法:只存恢复点(行号、ID),不存完整数据。

反例二:忘记实现 ItemStream

// ❌ Reader 没实现 ItemStream → 框架无法调用 open/update/close
public class MyReader implements ItemReader<String> {
    // 只实现了 read() → 无法保存/恢复状态
}

正确做法:需要状态管理的 Reader/Writer 必须实现 ItemStream,在 open() 中恢复,在 update() 中保存。

面试高频考点

Q1:ExecutionContext 何时被持久化?

每个 Chunk 提交后,框架调用 ItemStream.update(),然后 JobRepository 将 Step ExecutionContext 序列化到 BATCH_STEP_EXECUTION_CONTEXT 表。Job ExecutionContext 在 Job 执行结束时持久化。

Q2:如何跨 Step 传递数据?

通过 Job ExecutionContext:stepExecution.getJobExecution().getExecutionContext().put("key", value)。Job 内的所有 Step 都能访问这个共享的 ExecutionContext。


上一章:Step 配置下一章:ItemReader 详解

上一页
Step 配置
下一页
ItemStream 与状态管理