Spring Blog

Spring Batch 6.1.0-RC1 and 6.0.6 available now!

8.5内容质量

TL;DR · AI 摘要

Spring Batch 6.1.0-RC1发布,包含依赖升级、@JobParameter注解和新组件,提升开发效率与类型安全性。

核心要点

  • Spring Framework升级至7.1.0-RC1,增强生态兼容性
  • @JobParameter注解简化参数注入,替代复杂SpEL表达式
  • PageableItemReader通过类型安全替代RepositoryItemReader

结构提纲

按章节快速跳转。

  1. 宣布Spring Batch 6.1.0-RC1和6.0.6正式发布,包含多项改进。

  2. 升级Spring Framework至7.1.0-RC1及多个子项目至RC1版本。

  3. 提供更简洁的参数注入方式,替代原有SpEL表达式。

  4. 引入PageableItemReader等组件,提升类型安全性与开发效率。

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • Spring Batch 6.1.0-RC1更新
    • 依赖升级
      • Spring Framework 7.1.0-RC1
    • 新特性
      • @JobParameter注解
      • PageableItemReader组件

金句 / Highlights

值得收藏与分享的关键句。

#Spring Batch#Java#框架#版本更新
打开原文

我谨代表团队及所有贡献者,高兴地宣布 Spring Batch 6.1.0-RC1 和 6.0.6 现已通过 Maven Central 发布!这些版本带来了许多新功能、改进和错误修复。如需查看完整的变更列表,请查阅发布说明:6.1.0-RC1 和 6.0.6。

本文将带您了解 6.1.0-RC1 的主要变更内容:

  • 依赖项升级
  • 用于注入作业参数的 @JobParameter 注解
  • 新增 PageableItemReader
  • JsonLineMapper 与 JSON 库无关
  • 新增 JsonLineAggregator
  • 新增 RemoteChunkingManagerBuilder
  • 新增 DefaultChunkProcessor
  • JDBC DAO 迁移到 JdbcClient
  • 性能优化

依赖项升级

Spring 依赖项已升级至以下版本:

  • Spring Framework 7.1.0-RC1
  • Spring Integration 7.2.0-RC1
  • Spring Data 2026.1.0-RC1
  • Spring LDAP 4.2.0-RC1
  • Spring AMQP 4.2.0-RC1
  • Spring Kafka 4.2.0-RC1
  • Micrometer 1.18.0-RC1

用于注入作业参数的 `@JobParameter` 注解

以前将作业参数注入作业或步骤作用域的 Bean 需要使用 SpEL 表达式,这种表达式仅在运行时进行求值且容易出错。新的 @JobParameter 注解提供了更简洁的替代方案,默认将作业参数名称设置为注解字段或参数的名称:

code
@Bean
@StepScope
public FlatFileItemReader<Foo> fooReader(@JobParameter String inputFile) {
    return new FlatFileItemReaderBuilder<Foo>()
            .resource(new FileSystemResource(inputFile))
            // 其他属性省略
            .build();
}

这替代了 @Value("#{jobParameters['inputFile']}")。名称也可以显式指定,例如 @JobParameter("input.file.name")。与它替代的表达式一样,它适用于在作业或步骤作用域中定义的 Bean。当方法或构造函数参数上省略名称时,代码必须使用 -parameters 标志进行编译,这是 Spring Boot Maven 和 Gradle 插件的默认设置。更多详情请参见 issue #5563。

新增 `PageableItemReader`

PageableItemReader 是 RepositoryItemReader 的类型安全替代方案。它不通过名称反射性地调用仓库方法,而是使用从 Spring Data Pageable 到 Slice 的函数,该函数在编译时进行检查:

code
PageableItemReader<Customer> reader = new PageableItemReaderBuilder<Customer>()
        .query(customerRepository::findAll)
        .sort(Sort.by(Sort.Direction.ASC, "id"))
        .pageSize(100)
        .name("customerReader")
        .build();

排序也可以通过 Spring Data 的 类型安全属性引用 来实现,例如 Sort.by(Sort.Direction.ASC, Customer::getId),这种方式消除了基于字符串的属性名称。

`JsonLineMapper` 与 JSON 库无关

JsonLineMapper 以前只能将 JSON Lines 文件的每一行映射到 Map<String, Object>,并且只能使用 Jackson 实现。现在它已成为泛型,并委托给新的 JsonObjectUnmarshaller 来转换行,这使其独立于 JSON 库,能够将行映射到任何类型,例如领域类或记录。提供了基于 Jackson 和 Gson 的实现:

code
record Trade(String isin, int quantity, double price, String customer) {
}

FlatFileItemReader<Trade> reader = new FlatFileItemReaderBuilder<Trade>()
        .name("tradeReader")
        .resource(resource)
        .lineMapper(new JsonLineMapper<>(new JacksonJsonObjectUnmarshaller<>(Trade.class)))
        // 或 .lineMapper(new JsonLineMapper<>(new GsonJsonObjectUnmarshaller<>(Trade.class)))
        .build();

无参数构造函数和接受 JsonMapper 的构造函数已弃用,取而代之的是 JsonLineMapper(JsonObjectUnmarshaller)。这些旧构造函数将在 7.0 版本中被移除。这对使用原始 JsonLineMapper 类型并直接在其上调用 mapLine 的代码来说是一个源代码和二进制兼容性破坏性变更。仅将映射器传递给读取器的代码只需重新编译即可,无需其他更改。更多细节请参见 参考文档。

新的 `JsonLineAggregator`

JsonLineAggregator 是 JsonLineMapper 的写入端对应实现:一个 LineAggregator,它使得通过 FlatFileItemWriter 将项目写入 JSON Lines 文件成为可能。与映射器类似,它也与 JSON 库无关,因为它将项目转换委托给 JsonObjectMarshaller,因此可以使用 Jackson 或 Gson 实现:

code
FlatFileItemWriter<Trade> writer = new FlatFileItemWriterBuilder<Trade>()
        .name("tradeWriter")
        .resource(new FileSystemResource("trades.jsonl"))
        .lineAggregator(new JsonLineAggregator<>(new JacksonJsonObjectMarshaller<>()))
        .build();

新的 `RemoteChunkingManagerBuilder`

RemoteChunkingManagerBuilder 是 RemoteChunkingWorkerBuilder 的对应实现,用于配置远程分块设置中的管理器步骤。它仅包含与远程分块相关的特定设置(通道、消息模板、最大等待超时次数和节流限制),而 chunk(int) 方法会返回一个标准的 ChunkOrientedStepBuilder 用于其他所有配置。生成的步骤是一个 ChunkOrientedStep,构建器不会暴露任何弃用的 API:

code
@Bean
public Step managerStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
    return new RemoteChunkingManagerBuilder("managerStep", jobRepository)
            .outputChannel(requests())
            .inputChannel(replies())
            .chunk(100)
            .reader(itemReader())
            .transactionManager(transactionManager)
            .build();
}

RemoteChunkingManagerStepBuilder 和 RemoteChunkingManagerStepBuilderFactory 已被弃用,取而代之的是当前的构建器,计划在 7.0 版本中移除。需要注意的是,事务管理器默认使用无资源的实现,而之前的工厂类使用的是应用上下文中的事务管理器,因此如果 manager step 的项读取器需要事务资源,必须显式设置。

新的 `DefaultChunkProcessor`

由于 SimpleChunkProcessor 是 6.0 版本中遗留的基于块处理引擎的一部分,已被弃用。但此前它仍是处理步骤外块(例如远程分块工作者或 ChunkTaskExecutorItemWriter)的唯一方式。DefaultChunkProcessor 是这些场景的替代方案。它是一个最小化的 ChunkProcessor,对块中的每个项应用可选的 ItemProcessor,并通过 ItemWriter 写入结果,例如 new DefaultChunkProcessor<>(itemProcessor, itemWriter)。与 SimpleChunkProcessor 不同的是,它不支持监听器且不记录指标。RemoteChunkingWorkerBuilder 现在内部使用它。

JDBC DAO 迁移到 `JdbcClient`

作业仓库 DAO 的 JDBC 实现(JdbcJobInstanceDao、JdbcJobExecutionDao、JdbcStepExecutionDao 和 JdbcExecutionContextDao)现在使用 Spring Framework 的 JdbcClient 替代 JdbcOperations 来访问批处理元数据表。这为它们提供了命名参数和用于查询、更新和批量更新的单一流畅 API。客户端现在是必需的协作对象,因此需要通过构造函数传递:

code
JdbcJobInstanceDao jobInstanceDao = new JdbcJobInstanceDao(JdbcClient.create(dataSource));

通过 @EnableBatchProcessing、@EnableJdbcJobRepository、JdbcDefaultBatchConfiguration、JdbcJobRepositoryFactoryBean 或 JobExplorerFactoryBean 配置作业仓库的应用程序不受影响,因为这些配置会自行构建 DAO。但对于直接实例化这些 DAO 的代码,这是一个源码和二进制兼容性破坏性变更,因为它们的无参数构造函数已被移除。因此,AbstractJdbcBatchMetadataDao#setJdbcTemplate(JdbcOperations) 和 #getJdbcTemplate() 已被弃用,取而代之的是新的构造函数和 #getJdbcClient()。更多细节请参见 issue #4804。

性能改进

此版本对作业仓库和分片步骤的多个部分进行了优化,使其运行更快且内存占用更少。以下是主要改进点。

作业启动与重启

重启作业时,以前需要读取其作业实例的完整历史记录,加载所有过去的执行及其所有步骤。因此,作业重启的频率越高,每次重启所需的 SQL 语句就越多:在 6.0.4 版本中,相同失败作业的第 12 次重启会生成 130 条语句,每次过去的执行多出 8 条。现在仅检查最后一次执行,因此重启的成本不再依赖于历史记录。此优化也适用于 6.0.6 版本。

查找作业实例的执行也更快了。JdbcJobExecutionDao#findJobExecutions 以前需要为每个执行运行 3 次额外查询(N 次执行需要 1 + 3N 次查询)。现在无论 N 的值如何,只需要 2 次查询。对于有 10 次执行的作业实例,查询次数从 31 次减少到 2 次,大约减少 15 倍。getLastJobExecution 从 4 次查询减少到 2 次,减少一半。

[分区步骤](https://spring.io/blog/2026/10/22/spring-batch-6#partitioned-steps)

MessageChannelPartitionHandler 会定期向作业库查询其工作者是否完成。每次查询时,它以前会加载每个工作者的步骤执行,并且每个工作者都会附带整个作业执行及其所有步骤执行。当分区数量较多时,内存消耗会迅速增加:在某个案例中,一个包含 837 个分区的步骤在内存中保留了 837 × 837 = 700,569 个 StepExecution 对象,负责轮询工作者的节点使用了 15 GB 内存,是执行相同工作量的另一节点的 10 倍。

现在,处理器仅通过新方法 JobRepository#countRunningStepExecutions(Collection<Long>) 统计正在运行的工作者数量,并在所有工作者完成时一次性加载作业执行。在相同案例中,对象数量从 700,569 个减少到 837 个,减少 837 倍,同时消除了轮询节点的额外内存占用:内存使用量减少约 10 倍。新方法为 default 方法,因此现有 JobRepository 实现仍可正常工作,若不支持计数功能,处理器会回退到之前的逻辑。

[JDBC 作业库](https://spring.io/blog/2026/10/22/spring-batch-6#jdbc-job-repository)

一些 JDBC DAO 的查询也被移除了。每次调用时,每个方法都能节省一次查询:

  • 加载作业执行时不再运行单独的查询来查找其作业实例 ID,因为该 ID 已包含在 JOB_EXECUTION 表中。
  • 更新作业执行时不再先运行 SELECT COUNT(*) 来检查行是否存在,因为更新操作本身会报告行缺失。
  • JdbcJobExecutionDao#synchronizeStatus(StepExecution) 节省一次查询。
  • 因并发更改导致作业或步骤执行更新失败时,不再运行额外查询仅用于在错误信息中显示当前版本。

后两个优化也适用于 6.0.6 版本。

[反馈](https://spring.io/blog/2026/10/22/spring-batch-6#feedback)

感谢所有参与此次发布的贡献者!随着我们逐渐接近 6.1 GA 版本的发布,我们期待在 Github Issues、Github Discussions 和 X 上收到您的反馈。

  • * *

Spring Batch 官网|GitHub 源码|参考文档