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

    • 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章 运维与监控

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

Job 详解

本章定位:掌握 Spring Batch 的顶级概念 Job——它的职责边界、组成结构、配置方式以及与 JobInstance/JobExecution 的协作机制。

定义与作用

Job 是 Spring Batch 中的顶级批处理概念,封装了整个批处理过程的抽象。一个 Job 由一到多个 Step 按顺序或条件编排组成,是启动批处理任务的基本单元。

飞翔科技架构师白歌在团队培训中这样定义:

"Job 是你对整个批处理任务的'剧本'——第一幕做什么、第二幕做什么、什么情况下跳到备选路径。Step 是每一幕的具体内容。你不需要写主循环和异常处理,Spring Batch 帮你演完这出戏。"

Job 的职责边界:

Job 负责Job 不负责
定义 Step 的执行顺序和条件分支具体的数据读取/处理/写入(那是 Step 的职责)
注册监听器(JobExecutionListener)单个 Chunk 的事务管理(那是 Step 的内部机制)
配置 JobParametersIncrementer数据库连接和资源管理
设置是否可重启(restartable)作业调度和定时触发(那是外部调度器的职责)

核心原理

Job 的执行流程

Job 与 Step 的关系

Job
├── Step 1: 数据校验(Tasklet)
├── Step 2: 数据导入(Chunk: Reader → Processor → Writer)
│   ├── Chunk 1: 50 条 → COMMIT
│   ├── Chunk 2: 50 条 → COMMIT
│   └── ...
└── Step 3: 发送通知(Tasklet)

关键规则:

规则说明
每个 Job 至少包含一个 Step没有 Step 的 Job 没有意义
Step 按声明顺序执行除非通过 on() 指定条件分支
一个 Step 失败不等于 Job 立即失败通过条件分支可跳转到恢复 Step
Job 是单例 Bean但每次 run() 创建新的 JobInstance 和 JobExecution

完整示例

场景一:飞翔科技——学生数据导入的三阶段 Job

背景:产品经理孔蓝设计的学生数据导入流程包含三个阶段——先校验文件格式、再导入数据、最后生成导入报告。小崔用 Job 编排这三个 Step。

@Configuration
@EnableBatchProcessing
public class StudentImportJobConfig {

    @Bean
    public Job studentImportJob(JobRepository jobRepository,
                                 Step validateStep,
                                 Step importStep,
                                 Step reportStep,
                                 JobExecutionListener listener) {
        return new JobBuilder("studentImportJob", jobRepository)
                .incrementer(new RunIdIncrementer())
                .listener(listener)
                .start(validateStep)       // Step 1: 校验 CSV 格式
                .next(importStep)           // Step 2: 导入数据
                .next(reportStep)           // Step 3: 生成报告
                .build();
    }
}

运行结果:

Job: [studentImportJob] started with run.id=1
  JobExecutionListener.beforeJob() → 作业开始,文件: students_20260613.csv
  Step 1 [validateStep]: COMPLETED (CSV 格式正确,共 5000 行)
  Step 2 [importStep]: COMPLETED (read=5000, write=4980, skip=20)
  Step 3 [reportStep]: COMPLETED (报告已生成: import_report_20260613.txt)
  JobExecutionListener.afterJob() → 作业完成,耗时: 12.3s, 状态: COMPLETED

场景二:带条件分支的 Job——处理导入失败

背景:运营部高英反映有时候 CSV 文件格式有严重问题(如缺少必填列),此时不应该继续导入,而是应该发送告警邮件。

@Bean
public Job studentImportWithFailureHandling(JobRepository jobRepository,
                                              Step validateStep,
                                              Step importStep,
                                              Step reportStep,
                                              Step alertStep) {
    return new JobBuilder("studentImportWithFailureHandling", jobRepository)
            .incrementer(new RunIdIncrementer())
            .start(validateStep)
                .on("FAILED").to(alertStep)     // 校验失败 → 发告警
            .from(validateStep)
                .on("*").to(importStep)          // 校验成功 → 导入
            .from(importStep)
                .on("COMPLETED").to(reportStep)  // 导入成功 → 报告
            .from(importStep)
                .on("FAILED").to(alertStep)      // 导入失败 → 告警
            .end()
            .build();
}

运行结果(校验失败场景):

Job: [studentImportWithFailureHandling] started
  Step 1 [validateStep]: FAILED (缺少必填列: email)
  Step 4 [alertStep]: 已发送告警邮件到 ops@feixiang.net
  Step 4 [alertStep]: COMPLETED
Job: [studentImportWithFailureHandling] COMPLETED (通过告警路径结束)

易错场景与避坑

反例一:在 Job Bean 中写业务逻辑

小崔刚学 Spring Batch 时,把数据处理逻辑写在了 Job 的 @Bean 方法里:

// ❌ 错误:Job 的 Bean 方法只应做配置,不应包含运行时逻辑
@Bean
public Job badJob(JobRepository jobRepository, Step step) {
    // ❌ 这些代码在 Bean 初始化时执行,不是在 Job 运行时执行!
    List<String> lines = Files.readAllLines(Paths.get("data.csv"));
    for (String line : lines) {
        processLine(line);
    }
    return new JobBuilder("badJob", jobRepository)
            .start(step)
            .build();
}

问题:@Bean 方法在 Spring 容器初始化时执行,不是 Job 运行时。数据处理逻辑必须在 Step 内部的 Reader/Processor/Writer 或 Tasklet 中实现。

反例二:restartable 设置不当

// ❌ 设置为不允许重启,但业务需要重启能力
@Bean
public Job job(JobRepository jobRepository, Step step) {
    return new JobBuilder("job", jobRepository)
            .preventRestart()  // 禁止重启
            .start(step)
            .build();
}
// 第一次执行失败后,第二次直接用相同参数启动 → JobRestartException

正确做法:只在确实不需要重启的场景(如发送一次性通知)才设置 preventRestart()。大多数 ETL 作业应该保持默认可重启。

面试高频考点

Q1:Job 和 Step 的生命周期是如何关联的?

Job 的生命周期由其中的 Step 的执行结果决定。所有 Step 成功完成 → Job COMPLETED。任一 Step 失败且没有条件分支处理 → Job FAILED。Step 之间有先后顺序和条件路由,Job 负责整体编排。

Q2:如何禁止同一个 Job 的重复执行?

Spring Batch 设计上防止了同一 JobInstance 的重复执行(COMPLETED 后不可再启动)。通过使用 RunIdIncrementer 或在 JobParameters 中加入时间戳,可以保证每次执行产生新的 JobInstance,实现"每天跑同一个 Job"而不冲突。

Q3:Job 可以动态添加 Step 吗?

Job 是静态定义(Spring Bean),在容器启动时就确定了 Step 编排。但可以通过 JobExecutionDecider 在运行时动态决定执行路径,或者使用 Flow 实现更灵活的编排。如果需要完全动态的 Step 组合,考虑使用 JobBuilder 在 JobLauncher.run() 之前编程式构建。


上一章:Tasklet 处理模型下一章:Job 配置

下一页
Job 配置