欢迎光临
我们一直在努力

【SpringBoot 3.x 第70节】你以为把 Spring Boot 升到 3.x 就算“完成升级”了?那 Spring Batch 5.x 这些坑你打算怎么填?

🏆本文收录于《滚雪球学SpringBoot 3》,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。    本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】,你想学习的都被收集在内,快速投入学习!!两不误。    若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》,涵盖SpringBoot所有版本教学文章。

演示环境说明:

  • 开发工具:IDEA 2021.3
  • JDK版本: JDK 17(推荐使用 JDK 17 或更高版本,因为 Spring Boot 3.x 系列要求 Java 17,Spring Boot 3.5.4 基于 Spring Framework 6.x 和 Jakarta EE 9,它们都要求至少 JDK 17。)
  • Spring Boot版本:3.5.4(于25年7月24日发布)
  • Maven版本:3.8.2 (或更高)
  • Gradle:(如果使用 Gradle 构建工具的话):推荐使用 Gradle 7.5 或更高版本,确保与 JDK 17 兼容。
  • 操作系统:Windows 11

全文目录:

    • 前言
    • 0. 升级这事儿,到底在升级什么?(别被“版本号”骗了)
    • 1) Java 17:批处理代码怎么“更简化”?(少写点废话,多表达意图)
      • 1.1 用 record 写 Job 参数(告别一屏 getters/setters)
      • 1.2 Text Blocks:SQL/JSON/模板字符串终于不“反人类”
      • 1.3 Pattern Matching for instanceof:错误处理/事件分发更顺滑
      • 1.4 Switch Expressions:把“状态机味儿”写得更像人话
    • 2) 元数据表结构变更与迁移:真正的“生死线”在这里
      • 2.1 先搞明白:Spring Batch 元数据表到底存什么?
      • 2.2 Spring Batch 5 为什么要改元数据表?
      • 2.3 迁移脚本在哪里?别自己瞎写(真的,别)
      • 2.4 最容易踩的雷:BATCH_JOB_EXECUTION_PARAMS 的列语义变化
      • 2.5 一个更“工程化”的迁移策略(我一般这么干,比较稳)
    • 3) GraalVM Native Image:Spring Batch 到底能不能“原生化”?(能,但别幻想无痛)
      • 3.1 Spring Boot 的“已知限制”:别和物理规律对着干
      • 3.2 Spring Batch 在 Native Image 下的典型难点(很现实)
        • 难点 A:反射 + 序列化(ExecutionContext / 参数类型)
        • 难点 B:动态加载资源(SQL 脚本、扁平文件模板、Mapping 文件)
        • 难点 C:运行时装配(条件化配置 / Profile)
      • 3.3 突破口:AOT + Tracing Agent,找到缺的“提示”
      • 3.4 观测能力反而更“顺滑”:Batch 5 的 Tracing 很值得顺手用上
    • 4) 分布式批处理任务监控:别让“分布式”变成“分裂式”
      • 4.1 指标层:Micrometer + Actuator,把“看不见”变成“看得见”
      • 4.2 追踪层:Batch 5 的 tracing 让“分布式定位”不再靠猜
      • 4.3 真·分布式批处理:Spring Cloud Data Flow 给你一条“更完整的路”
      • 4.4 分布式监控的“工程落地清单”(我一般要求团队至少做到这些)
        • 第一层:元数据一致性(JobRepository 是唯一真相)
        • 第二层:指标(Metrics)
        • 第三层:追踪(Tracing)
        • 第四层:日志(Logs)
    • 5) 用一段“更贴近 Batch 5”的示例,把这些点串起来(别光讲理论)
      • 5.1 参数 record + JobParameters 组装
      • 5.2 SQL 用 text blocks(可读性上升一大截)
      • 5.3 失败分类:instanceof 模式匹配 + switch 表达式(写起来像“说话”)
      • 5.4 观测:Batch 5 的 tracing 与 metrics 是“标配思维”
    • 6) 最后给你一份“升级路线图”(不花哨,但很抗揍)
      • 第一步:先让它“能编译”
      • 第二步:再让它“能跑 Job”
      • 第三步:让它“跑得可控”
      • 第四步:再考虑 Native Image(别本末倒置)
    • 结语:升级不是“赶版本”,是“把系统的可维护性拉上来”
    • 🧧福利赠与你🧧
    • 🫵 Who am I?

前言

我得先承认一件事:每次升级大版本,我嘴上都说“稳得很”,手上却诚实得要命——先把咖啡续满,再把回滚方案写好☕😅。    尤其是从 Spring Boot 2.x 迈到 Spring Boot 3.x 这一步,表面上像是“依赖换一换、包名 jakarta 一改就完事”,但只要你项目里沾了 Spring Batch,你就会很快意识到:Batch 的升级不是“搬家”,更像“换房还要重装水电”。    今天我就按你给的大纲,围绕 Spring Boot 3.x + Spring Batch 5.x 的批处理更新,把我认为最关键、最容易翻车、也最值得趁这次升级顺手“变强”的几块,掰开揉碎讲清楚:

  • Java 17 如何让批处理代码更“人类友好”(别再写一堆样板代码了,求求)
  • 元数据表结构的变更与迁移(这个不处理,升级后跑起来就“当场去世”)
  • GraalVM Native Image 对 Spring Batch 的支持限制与突破(能跑≠好跑,别想当然)
  • 分布式批处理任务的监控(别等凌晨 3 点告警响了才想起“我没监控”)

0. 升级这事儿,到底在升级什么?(别被“版本号”骗了)

Spring Batch 5 的本质变化,不是“API 改了几个方法名”这么简单,而是它站在了更“现代”的地基上:

  • JDK 17 成为基线(Spring Batch 5 基于 Spring Framework 6,而 Spring Framework 6 要求 Java 17 起步)
  • 依赖体系整体跃迁:Spring 6、Spring Integration 6、Spring Data 3、Micrometer 1.10,以及 Jakarta EE 9 迁移等
  • 批处理基础设施配置发生变化:DataSource / TransactionManager 的要求更明确,老的内存型实现被移除
  • 监控层面开始更系统地拥抱 Micrometer 指标与 Observation/Tracing(不仅能看指标,还能“追踪”)

所以你升级 Spring Batch 5,实际上是在同时升级:语言特性(Java 17)、框架基座(Spring 6 / Jakarta)、运行时观测体系(Micrometer/Observation)、以及——最容易被忽略但最致命的——元数据表结构与数据语义。

1) Java 17:批处理代码怎么“更简化”?(少写点废话,多表达意图)

Batch 项目最折磨人的地方之一,是它天然就会产生很多“胶水代码”:参数对象、统计对象、错误对象、事件对象……以前你不写 equals/hashCode/toString 就不踏实,写了又嫌烦。   Java 17 给你的第一把顺手小刀,就是 Records:用来表达“数据载体”再合适不过。OpenJDK 的 JEP 395 就把 records 定义为“不可变数据的透明载体(transparent carriers for immutable data)”。

1.1 用 record 写 Job 参数(告别一屏 getters/setters)

假设我们有一个导入任务:从 CSV 导入用户数据,参数包括 filePath、dryRun、tenantId。

// Java 17 record:表达“我就是一坨数据”,别逼我写样板
public record ImportUsersParams(
String filePath,
boolean dryRun,
String tenantId
) {}

以前你可能会写个 POJO,再加 Builder,再加校验;现在 record 的好处是:

  • 语义清晰:它就是参数集合
  • 不可变:批处理参数天然就更适合不可变(少出幺蛾子)
  • 自动拥有 toString/equals/hashCode(你排查问题时会感谢它)

然后你可以把它塞进 JobParameters(下面我会讲 Batch 5 对参数持久化的变化,别急😉)。

1.2 Text Blocks:SQL/JSON/模板字符串终于不“反人类”

批处理里常见场景:JdbcCursorItemReader / JdbcBatchItemWriter 写 SQL。 以前 SQL 字符串要么一行写到天荒地老,要么到处 \\n + +,看着就想叹气。

Java 的 Text Blocks(JEP 378)就是为这种“多行文本字面量”设计的:减少转义,让格式可控。

String sql = """
SELECT id, name, email
FROM users
WHERE tenant_id = ?
AND created_at >= ?
ORDER BY id
"""
;

你别小看这个变化——当你凌晨定位数据错乱时,少一层字符串拼接,就少一次误读。真的。

1.3 Pattern Matching for instanceof:错误处理/事件分发更顺滑

批处理里最常见的“啰嗦代码”之一,是对异常、结果类型做分支判断。

Java 的 instanceof 模式匹配(JEP 394)让这事更简洁也更安全。

void handleFailure(Exception ex) {
if (ex instanceof java.sql.SQLException sqlEx) {
log.warn("DB 异常,SQLState={}", sqlEx.getSQLState(), sqlEx);
} else if (ex instanceof IllegalArgumentException iae) {
log.warn("参数异常:{}", iae.getMessage());
} else {
log.error("未知异常", ex);
}
}

以前你要 instanceof 后再强转,现在直接拿到强类型变量,代码干净不少。

1.4 Switch Expressions:把“状态机味儿”写得更像人话

批处理里经常要按 Step/Job 状态做策略,比如:失败重试、跳过、报警等级等。

Switch Expressions(JEP 361)能让这种逻辑更像表达式,不像“老式分支迷宫”。

enum AlarmLevel { NONE, INFO, WARNING, CRITICAL }

AlarmLevel level = switch (status) {
case "COMPLETED" -> AlarmLevel.NONE;
case "FAILED" -> AlarmLevel.CRITICAL;
case "STOPPED" -> AlarmLevel.WARNING;
default -> AlarmLevel.INFO;
};

说人话:Java 17 让你写 Batch 代码时,更容易把“业务意图”写出来,而不是把“语言仪式”写出来。而升级到 Spring Batch 5,本来就要求 Java 17 基线,你不顺便把这些“武器”捡起来用一下,多少有点亏。

2) 元数据表结构变更与迁移:真正的“生死线”在这里

我见过最离谱的升级事故是:

代码编译通过、应用启动成功、接口也能访问,然后一跑 Job —— 数据库报错,元数据表不匹配,JobRepository 直接炸。 那种感觉就像:你以为车修好了,结果一踩油门方向盘掉了🙂。

2.1 先搞明白:Spring Batch 元数据表到底存什么?

Spring Batch 的元数据表,基本对应它的核心领域对象:

  • JobInstance ↔ BATCH_JOB_INSTANCE
  • JobExecution ↔ BATCH_JOB_EXECUTION
  • JobParameters ↔ BATCH_JOB_EXECUTION_PARAMS
  • StepExecution ↔ BATCH_STEP_EXECUTION
  • ExecutionContext ↔ BATCH_JOB_EXECUTION_CONTEXT / BATCH_STEP_EXECUTION_CONTEXT 这些映射关系在 schema 附录里写得很清楚。

换句话说:它不是“日志表”,而是“批处理可恢复性/可追踪性”的数据库心脏。你要 restart、要追溯、要统计,都靠它。

2.2 Spring Batch 5 为什么要改元数据表?

核心原因之一,是 JobParameters 能支持更多类型,不再局限于过去常见的四种预定义类型(Long/Double/String/Date)那一套——这会直接影响 BATCH_JOB_EXECUTION_PARAMS 的存储方式。很多升级文章会提,但你最好信官方:迁移指南和参考文档会明确告诉你有哪些变化、要怎么迁。

在 Spring Batch 5 的“新增特性”里,也强调了依赖升级与基础设施配置更新,意味着整个批处理“基础设施层”更现代、更一致,但也更严格。

2.3 迁移脚本在哪里?别自己瞎写(真的,别)

Spring Batch 官方文档明确说:迁移 DDL 脚本在 core jar 里: org/springframework/batch/core/migration,并按引入版本号分文件夹组织。

这句信息很关键,因为它意味着:

  • 你不需要“凭感觉”写 alter table
  • 你要做的是:找到你当前版本 → 目标版本之间的迁移脚本链路
  • 对应数据库(PostgreSQL/Oracle/MySQL…)会有不同脚本

我一般会把它接入 Flyway/Liquibase 来跑迁移,避免手工执行的不可控。

2.4 最容易踩的雷:BATCH_JOB_EXECUTION_PARAMS 的列语义变化

很多团队升级翻车,就翻在这张表上,因为它关系到 JobInstance 的“身份”与 JobExecution 的参数持久化。schema 附录对 BATCH_JOB_EXECUTION_PARAMS 的描述里提到:它是反范式化存储,会用列来指示类型。

而在 Spring Batch 5 的新表结构里,你会看到参数类型的表达方式发生了变化:例如 PARAMETER_TYPE 这类字段期望的是 Java 类名(通常是全限定名),这和旧版本里常见的 TYPE_CD = 'LONG'/'STRING' 那种写法完全不是一回事。

迁移思路(举例):把旧的 TYPE_CD 映射为 Java 类型全限定名

  • LONG → java.lang.Long
  • STRING → java.lang.String
  • DATE → java.util.Date(或你项目采用的时间类型)
  • DOUBLE → java.lang.Double

给你一个“能落地”的 SQL 示例(以旧字段 TYPE_CD → 新字段 PARAMETER_TYPE 为例,实际列名以你的版本脚本为准):

UPDATE BATCH_JOB_EXECUTION_PARAMS
SET PARAMETER_TYPE = CASE
WHEN TYPE_CD = 'LONG' THEN 'java.lang.Long'
WHEN TYPE_CD = 'STRING' THEN 'java.lang.String'
WHEN TYPE_CD = 'DOUBLE' THEN 'java.lang.Double'
WHEN TYPE_CD = 'DATE' THEN 'java.util.Date'
ELSE PARAMETER_TYPE
END;

为什么我这么强调“语义”?因为:

  • DDL 迁完只是“表能建起来”
  • 数据不迁,你历史 JobInstance 可能无法正确识别/重跑
  • 如果你的系统依赖历史参数做幂等或审计,数据迁移失败会引发更隐蔽的问题

2.5 一个更“工程化”的迁移策略(我一般这么干,比较稳)

我自己的做法通常分四步:

  • 冻结写入窗口(短暂停止新 Job 执行,避免迁移过程中元数据还在变化)
  • 跑官方 DDL migration 脚本(来自 core jar migration 目录)
  • 数据迁移补丁(尤其是 JobParameters 类型/值的转换)
  • 验收查询(抽样验证历史执行可查询、可 restart、参数可读)
  • 验收查询你可以这么设计:

    • 按 job_name 查最近 N 次 execution 的参数是否能被解析
    • 按某个 identifying 参数重建 JobInstance 是否一致
    • JobExplorer/JobOperator 是否能读取旧执行并重跑(Batch 5 对这些组件也有事务支持方面的更新)

    这部分听起来很“数据库运维”,但我想说句特别直白的话:

    批处理的稳定性,70% 是数据与元数据的一致性,不是代码写得多优雅。 代码写得再漂亮,元数据表结构不对,一样跑不起来;元数据迁错了,跑是能跑,但“可重启/可追溯”会慢性死亡。

    3) GraalVM Native Image:Spring Batch 到底能不能“原生化”?(能,但别幻想无痛)

    我第一次把一个带 Batch 的应用打成 Native Image 时,心态是这样的:

    • 构建前: “起飞!启动 30ms!” 🚀
    • 构建中:“怎么又缺反射提示?” 😵
    • 运行后:“能跑是能跑,但某些动态特性一碰就裂。” 🙂

    你要理解,Native Image 的世界观和 JVM 很不一样:它强调静态可达性分析,很多 JVM 时代“运行时才决定”的事,在原生镜像里必须提前声明。

    Spring Boot 官方文档在 Native Image 介绍里明确讲了:

    Spring 仍然可能需要反射(比如调用 private 方法上的注解),这时 Spring 会生成 reflection hints,并放在 META-INF/native-image 让 GraalVM 自动拾取。

    同时在 Advanced Topics 里也强调:Spring 的 AOT 引擎会生成很多提示,但有些场景仍需要额外处理,比如嵌套配置属性等。

    3.1 Spring Boot 的“已知限制”:别和物理规律对着干

    Spring Boot 官方也很坦率:GraalVM Native Image 还在快速发展,并不是所有库都天然支持;Spring 本身不会为所有第三方库提供提示,而是依赖“可达性元数据”(reachability metadata)生态。

    说人话:

    • 你项目里只要有大量反射、动态代理、SPI、运行时扫描、动态类加载,就需要额外提示或改实现
    • 不是 Spring “不行”,是 Native Image 的闭环假设决定了它对动态特性天生敏感

    3.2 Spring Batch 在 Native Image 下的典型难点(很现实)

    结合 Batch 的典型用法,难点经常出在这几块:

    难点 A:反射 + 序列化(ExecutionContext / 参数类型)

    Batch 的 ExecutionContext 会持久化状态;你如果把复杂对象塞进去,原生镜像可能需要序列化/反射提示。 尤其 Batch 5 又更灵活地支持参数类型,你更容易“顺手塞个自定义类型”,然后 Native Image 就开始皱眉。

    难点 B:动态加载资源(SQL 脚本、扁平文件模板、Mapping 文件)

    ItemReader/Writer 里常见 resource 读取,如果资源没被 AOT 识别进镜像,就会运行时报找不到。

    难点 C:运行时装配(条件化配置 / Profile)

    Native Image 下,某些依赖运行时环境的条件装配会受限,尤其是你写得比较“骚”的时候(比如通过反射扫描 classpath 生成 step)。

    3.3 突破口:AOT + Tracing Agent,找到缺的“提示”

    Spring Boot 的 Native Image Advanced Topics 提到:GraalVM 的 tracing agent 可以在 JVM 上拦截反射/资源/代理使用,从而生成相关 hints;Spring 会自动生成大部分,但 tracing agent 能快速定位缺失项。

    我一般这么干(思路层面,不贴一堆命令吓人):

  • 先用 JVM 跑一遍典型 Job 路径(包含你最复杂的 reader/writer、最容易触发反射的部分)
  • 打开 tracing agent 收集缺失提示
  • 把生成的 hint 合并进项目
  • 再构建 native image
  • 这就像:你让应用“自己供出它到底在哪些地方偷偷用了反射/代理/资源加载”。

    听着很刑侦,但确实好用😅。

    3.4 观测能力反而更“顺滑”:Batch 5 的 Tracing 很值得顺手用上

    Spring Batch 5 开始通过 Micrometer Observation API 提供追踪:默认使用 @EnableBatchProcessing 就启用 tracing,会为每个 job execution 创建 trace、每个 step execution 创建 span。

    而 Spring Batch 5 的 “What’s New” 也提到升级到 Micrometer 1.10 后,除了 metrics 还能有 tracing:每个 job 一个 span、每个 step 一个 span。

    这意味着:

    • 你做分布式批处理时,不止能看“成功/失败”,还能看“卡在哪个 step/哪个分区”

    • 在 Native Image 场景下,“更快启动 + 更强观测”是很现实的收益组合

    4) 分布式批处理任务监控:别让“分布式”变成“分裂式”

    讲分布式 Batch,我先抛个大实话:

    很多人做分布式批处理,只做到了“把任务跑到多台机器上”,却没做到“知道每台机器在干嘛”。 然后就出现一种很魔幻的局面:数据处理完了,但你不知道它怎么处理完的;数据处理错了,你也不知道错在哪一步🙂。

    Spring Batch 自 4.2 起就基于 Micrometer 提供批处理监控指标支持,并列出了开箱即用的指标(spring.batch.* 前缀),包括 job/step 的 timer、active long task timer,以及 item read/process/write 的计时。

    这就是分布式监控的第一块拼图:指标(Metrics)。

    4.1 指标层:Micrometer + Actuator,把“看不见”变成“看得见”

    Spring Boot Actuator 的 metrics 体系是围绕 Micrometer 做依赖管理和自动配置的,并支持 Prometheus、OTLP、Datadog 等多个监控系统。

    所以在 Boot 3.x + Batch 5.x 的组合里,一般是:

    • Batch 负责把 spring.batch.* 指标注册到 Micrometer registry
    • Boot Actuator 负责把 metrics 暴露/导出给监控后端

    我建议你至少做两件事:

    1)在应用里开启并暴露 actuator 指标端点(别只在本地开,线上也要有合规的暴露策略) 2)把指标导出到一个真正的时序数据库(Prometheus/Influx/Wavefront 等)

    指标端点本身是诊断用的,不是让你拿来当“生产抓取后端”的,这点在一些文档里也会强调。

    4.2 追踪层:Batch 5 的 tracing 让“分布式定位”不再靠猜

    Spring Batch 5 默认就能为 job/step 创建 trace/span(在启用 observability 时)。

    你在分布式执行(比如远程分区、多个 worker 实例)时,如果 trace context 能贯通,你会得到一种很爽的体验:

    • “这个 Job 总共 12 分钟”不重要
    • 重要的是:“第 7 分钟开始卡在 step=importUsers,partition=5,卡在 write” 这时候你才能去查:DB 慢?锁竞争?下游接口限流?某个 partition 数据倾斜?

    4.3 真·分布式批处理:Spring Cloud Data Flow 给你一条“更完整的路”

    如果你的分布式批处理不是“自己写个调度器”那种,而是希望有更体系化的部署与监控,Spring Cloud Data Flow 的 batch 监控指南明确提到:

    Spring Batch 提供额外的集成来暴露任务持续时间、速率、错误等指标,对已部署批处理作业的监控至关重要,并以Prometheus/InfluxDB/Wavefront 等时序库为例。

    你可以把它理解为:

    • Spring Batch:负责批处理语义与元数据一致性
    • Spring Boot + Micrometer:负责把观测数据标准化
    • Data Flow:负责更上层的任务编排/部署/可视化(看你是否需要)

    如果你项目规模已经到了“多人维护、多环境部署、任务很多、失败要追责”的程度,那 Data Flow 这种“平台化”的方案往往比你手搓一套 dashboard 更稳。

    4.4 分布式监控的“工程落地清单”(我一般要求团队至少做到这些)

    我把它分成四层,缺一层都像少一条腿:

    第一层:元数据一致性(JobRepository 是唯一真相)
    • 所有节点共享同一个 JobRepository(或有清晰的隔离策略)
    • 元数据表迁移正确(第二章说的那些)
    第二层:指标(Metrics)
    • 关键指标:spring.batch.job、spring.batch.step、spring.batch.item.* 等(Batch 开箱即用)
    • 导出到 Prometheus/Influx/OTLP 等(Boot Actuator + Micrometer)
    第三层:追踪(Tracing)
    • job/step span 要能串起来(Batch 5 支持)
    • 你要能从 trace 一路点到“具体 step 的耗时、失败点”
    第四层:日志(Logs)
    • 日志必须带 jobName、jobExecutionId、stepName、partitionId(如果有)
    • 统一结构化输出,便于关联 metrics/traces(这点你不做,排障时就会靠“猜”)

    我知道这听起来有点“管理味儿”,但真要扛线上批处理,你会发现:    监控不是“锦上添花”,是“你能不能睡觉”的分界线。

    5) 用一段“更贴近 Batch 5”的示例,把这些点串起来(别光讲理论)

    下面我用一个简化版场景:分区导入用户(单机也能跑,分布式也能扩),演示:

    • Java 17 record + text blocks
    • Batch 5 的 observability(指标 + tracing)
    • Job 参数更“类型化”的写法(也提醒你注意元数据迁移)

    5.1 参数 record + JobParameters 组装

    public record ImportUsersParams(String filePath, boolean dryRun, String tenantId) {}

    public class ParamsFactory {

    public static org.springframework.batch.core.JobParameters toJobParameters(ImportUsersParams p) {
    return new org.springframework.batch.core.JobParametersBuilder()
    .addString("filePath", p.filePath(), true)
    .addString("tenantId", p.tenantId(), true)
    .addString("dryRun", Boolean.toString(p.dryRun()), false)
    // 如果你要放更复杂类型,记得考虑 Batch 5 参数持久化方式与元数据迁移
    .toJobParameters();
    }
    }

    这里我故意把 dryRun 用字符串存:不是因为“不能存 boolean”,而是想强调——参数持久化要考虑迁移与可读性。Batch 5 对参数类型支持更灵活,但你也更需要规范。

    5.2 SQL 用 text blocks(可读性上升一大截)

    String selectSql = """
    SELECT id, name, email
    FROM staging_users
    WHERE tenant_id = :tenantId
    ORDER BY id
    """
    ;

    Text Blocks 的目的就是避免大量转义并保持格式可控。

    5.3 失败分类:instanceof 模式匹配 + switch 表达式(写起来像“说话”)

    enum FailType { DB, DATA, UNKNOWN }

    static FailType classify(Exception ex) {
    if (ex instanceof java.sql.SQLException) return FailType.DB;
    if (ex instanceof IllegalArgumentException) return FailType.DATA;
    return FailType.UNKNOWN;
    }

    static String advice(Exception ex) {
    return switch (classify(ex)) {
    case DB -> "数据库层面先看连接池/慢查询/锁竞争";
    case DATA -> "输入数据先做校验,必要时把坏数据隔离";
    case UNKNOWN -> "先把异常堆栈和 jobExecutionId 对齐再说";
    };
    }

    Switch Expressions 的 yield/表达式化能力是为这种“策略选择”场景准备的。

    5.4 观测:Batch 5 的 tracing 与 metrics 是“标配思维”

    只要你使用 @EnableBatchProcessing,Batch 5 默认就会启用 tracing(为 job 建 trace、为 step 建 span)。

    同时,Batch 也会把 spring.batch.* 指标注册进 Micrometer。

    再由 Boot Actuator 把这些指标暴露/导出。

    这意味着你如果做分布式 worker:

    • 每个 worker 自己暴露 metrics
    • Prometheus 去抓
    • Trace 通过 OTLP/Zipkin/Tempo 统一汇聚 你就能从一个 JobExecution 追到某个 step 的热点与失败点。

    6) 最后给你一份“升级路线图”(不花哨,但很抗揍)

    升级 Spring Boot 3.x + Batch 5.x,我建议按这个顺序来(我踩坑踩出来的):

    第一步:先让它“能编译”

    • 升级到 Java 17(Batch 5 的基线)
    • 处理 jakarta 包迁移(Boot 3 的常规动作)
    • 处理 Batch 5 的 API/配置变化(看 migration guide)

    第二步:再让它“能跑 Job”

    • 对照 schema 附录确认元数据表结构
    • 跑官方 migration DDL 脚本(core jar migration 目录)
    • 补参数表数据迁移(尤其参数类型字段语义)

    第三步:让它“跑得可控”

    • 接入 Micrometer + Actuator,确认 spring.batch.* 指标存在
    • 开启 tracing,让 job/step 有 trace/span

    第四步:再考虑 Native Image(别本末倒置)

    • 先把 JVM 版跑稳
    • 再用 AOT + hints + tracing agent 补齐缺口

    结语:升级不是“赶版本”,是“把系统的可维护性拉上来”

    我一直觉得批处理很像“城市的下水道”:平时没人夸你,出事一定先骂你。

    但 Spring Batch 5 + Boot 3 的升级,其实给了你一个很好的机会:

    • 用 Java 17 把代码写得更表达意图(records/text blocks/pattern matching/switch expressions)
    • 把元数据迁移做扎实,让“可重启/可追溯”真正可靠
    • 把 metrics + tracing 建起来,让分布式批处理不再靠“盲人摸象”
    • 如果你愿意,再去挑战 Native Image,把启动与资源占用优化到新的层级

    最后我想留一个有点“欠揍”的反问给你:    你们现在的批处理,失败以后是“可重跑”,还是“只能祈祷别再失败”?

    🧧福利赠与你🧧

      无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。

    最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。

    同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G PDF编程电子书、简历模板、技术文章Markdown文档等海量资料。

    ps:本文涉及所有源代码,均已上传至Gitee开源,供同学们一对一参考 Gitee传送门,同时,原创开源不易,欢迎给个star🌟,想体验下被🌟的感jio,非常感谢❗

    🫵 Who am I?

    我是 bug菌:

    • 热活跃于 CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等技术社区;
    • CSDN 博客之星 Top30、华为云多年度十佳博主&卓越贡献奖、掘金多年度人气作者 Top40;
    • 掘金、InfoQ、51CTO 等平台签约及优质作者;
    • 全网粉丝累计 30w+。

    更多高质量技术内容及成长资料,可查看这个合集入口 👉 点击查看 👈️ 硬核技术公众号 「猿圈奇妙屋」 期待你的加入,一起进阶、一起打怪升级。

    – End –

    赞(0)
    未经允许不得转载:171主机测评 » 【SpringBoot 3.x 第70节】你以为把 Spring Boot 升到 3.x 就算“完成升级”了?那 Spring Batch 5.x 这些坑你打算怎么填?
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址