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

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

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

@StepScope 与 @JobScope

本章定位:深入理解 Spring Batch 的两个自定义作用域,掌握延迟绑定、参数注入和状态上下文访问的完整用法。

定义与作用

@StepScope 和 @JobScope 是 Spring Batch 自定义的 Bean 作用域。它们使 Bean 的创建延迟到 Step/Job 启动时,从而能够注入运行时的 JobParameters 和 ExecutionContext 值。

@Bean
@StepScope
public FlatFileItemReader<Order> reader(
        @Value("#{jobParameters['input.file']}") String inputFile) {
    // inputFile 在 Step 启动时才可用
    return new FlatFileItemReaderBuilder<Order>()
            .resource(new FileSystemResource(inputFile))
            .build();
}

小崔的感悟:

"没 @StepScope 之前,你得在代码里手动获取 JobParameters——又丑又绕。有了它,直接 @Value 注入,像普通 Spring Bean 一样用,框架帮你延迟到正确的时机创建。"

核心原理

两个作用域对比

维度@StepScope@JobScope
生命周期单个 Step 执行期间单个 Job 执行期间
创建时机Step 启动时Job 启动时
可注入jobParameters、stepExecutionContextjobParameters、jobExecutionContext
适用 BeanItemReader、ItemProcessor、ItemWriterJob 级别的 Listener、Decider
典型场景动态文件路径、分区上下文作业级别配置

完整示例

场景一:@StepScope 绑定动态文件路径

@Configuration
public class StepScopeConfig {

    @Bean
    @StepScope
    public FlatFileItemReader<Order> orderReader(
            @Value("#{jobParameters['input.directory']}") String dir,
            @Value("#{jobParameters['input.date']}") String date) {
        // 用 JobParameters 动态拼接文件路径
        String filePath = dir + "/orders_" + date + ".csv";
        return new FlatFileItemReaderBuilder<Order>()
                .name("orderReader")
                .resource(new FileSystemResource(filePath))
                .delimited()
                .names("orderId", "customerName", "amount")
                .targetType(Order.class)
                .build();
    }
}

场景二:@StepScope 读取分区上下文

@Bean
@StepScope
public JdbcPagingItemReader<Order> partitionReader(
        DataSource dataSource,
        @Value("#{stepExecutionContext['minId']}") int minId,
        @Value("#{stepExecutionContext['maxId']}") int maxId) {
    // 每个 Worker 获取自己的分区范围
    return new JdbcPagingItemReaderBuilder<Order>()
            .name("partitionReader_" + minId + "_" + maxId)
            .dataSource(dataSource)
            .selectClause("SELECT *")
            .fromClause("FROM orders")
            .whereClause("WHERE id BETWEEN :minId AND :maxId")
            .parameterValues(Map.of("minId", minId, "maxId", maxId))
            .sortKeys(Map.of("id", Order.ASCENDING))
            .pageSize(500)
            .rowMapper(new BeanPropertyRowMapper<>(Order.class))
            .build();
}

场景三:@JobScope 注入到 Listener

@Component
@JobScope
public class JobReportListener implements JobExecutionListener {

    @Value("#{jobParameters['report.email']}")
    private String reportEmail;

    @Value("#{jobParameters['report.type']}")
    private String reportType;

    @Override
    public void afterJob(JobExecution jobExecution) {
        if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
            System.out.println("发送 " + reportType + " 报表到 " + reportEmail);
            // emailService.send(reportEmail, generateReport(reportType));
        }
    }
}

易错场景与避坑

反例一:无 @StepScope 时注入 JobParameters

// ❌ 没有 @StepScope → Bean 在容器启动时创建
@Bean
public FlatFileItemReader<Order> reader(
        @Value("#{jobParameters['input.file']}") String file) {
    // JobParameters 此时还不存在 → SpEL 解析失败
    return new FlatFileItemReaderBuilder<Order>()
            .resource(new FileSystemResource(file))
            .build();
}
// 容器启动报错: EvaluationException

反例二:@StepScope Bean 被单例 Bean 注入

// ❌ 单例 Service 注入 @StepScope Reader
@Service
public class BatchService {
    @Autowired
    private FlatFileItemReader<Order> reader; // ❌ 单例持有 StepScope Bean
    // 需要用 @Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
}

正确做法:@StepScope 默认使用 TARGET_CLASS 的 CGLIB 代理,Spring Boot 会自动处理。如果在纯 Spring 环境需要手动配置 ScopedProxyMode。

面试高频考点

Q1:@StepScope 的底层实现原理?

基于 Spring 的自定义作用域机制(Scope 接口)。StepScope 将 Bean 实例存储在 Step 的 stepContext 属性中,Step 启动时创建、Step 结束时销毁。SpEL 表达式 #{jobParameters['xxx']} 在 Bean 创建时解析。

Q2:为什么 Reader/Writer 默认应该是 @StepScope?

①可以注入 JobParameters 获取动态配置;②分区场景下每个 Worker 需要不同的数据范围,通过 #{stepExecutionContext['xxx']} 注入;③避免单例 Bean 在多次 Job 执行间共享状态导致线程安全问题。


上一章:ItemStream 与状态管理下一章:ItemReader 详解

上一页
ItemStream 与状态管理