实测 Spring Batch 6.0.5:Boot 启动时自动运行 Job,无参数的 Job 第二次启动因 JobInstanceAlreadyCompleteException 而启动失败;chunk=1 提交 5000 次耗时 2247 ms,chunk=100 提交 50 次 169 ms;处理器返回 null 即过滤;写第 250 条失败只回滚当前 chunk,已提交的 200 条保留;同参数的 Job 不能重复运行。
实测 Spring Batch 6.0.5 的容错:默认任何异常让步骤失败并回滚当前 chunk;processor 异常用 skip 就地跳过;超出 skipLimit 当前 chunk 整块回滚;writer 失败会回滚后逐条重写(writer 调用 13 次);retryLimit(n) 总尝试 n+1 次;重启时不可重启的读取器从头读,表里多出 200 行重复,而 JdbcCursorItemReader 从第 201 条继续。
实测多实例下的 @Scheduled 与 ShedLock 7.10.1(MySQL 锁表):不加锁两个实例 10 秒合计执行 18 次;加 @SchedulerLock 后同一时刻只有一个实例执行,最短间隔受 lockAtLeastFor 限制;任务比 lockAtMostFor 久时出现并发重叠;持有锁的实例被 kill -9 后另一个实例要等约 lockAtMostFor 才恢复;locked_by 默认是主机名。
实测 Spring Integration 7.0.6 的消息通道与端点:DirectChannel 轮询分发并在发送线程执行,默认 failover;PublishSubscribeChannel 默认一个订阅者失败后面的收不到;QueueChannel 的吞吐受 poller 间隔限制(6 条 1566 ms 对 30 ms);异步通道里的异常只出现在 errorChannel;@MessagingGateway 的 replyTimeout 在同步通道上不起作用;IntegrationFlow DSL 的 filter / transform / route。
实测 Spring Boot 4 的 spring.config.import 与配置文件加载顺序:location 是替换、additional-location 是追加;被导入的文件覆盖声明它的文件,多个 import 后面的赢;configtree 只有在内容里仅一个换行时才去掉末尾换行;默认位置分 classpath 与 file 两组,同组内 profile 专属文件整体高于普通文件,file 整组高于 classpath 整组。
实测 Spring Boot 4 的 @ConfigurationProperties:宽松绑定接受的写法(环境变量多一个下划线就绑定不上);列表被高优先级来源整体替换而 Map 合并;Duration、DataSize、boolean、枚举的转换规则;嵌套对象要加 @Valid 才校验,没有 hibernate-validator 时 @Validated 悄悄失效;只有 getter 的字段配了值会启动失败;record 的嵌套对象与集合缺失时是 null;绑定只发生一次。
实测 Spring Boot 4 的 profile:激活方式(命令行 > 系统属性 > 环境变量 > 配置文件)与 include、additionalProfiles、default 的行为;9 种 @Profile 写法的逐个对照(数组是「或」,带逗号的不是,名称区分大小写,混用 & 和 | 要加括号);profile 组的展开顺序;多个 profile 里后写的赢;写在 @PropertySource 里的 spring.profiles.active 读得到却不激活。
实测 SpringApplication.run 的启动阶段:Starting、EnvironmentPrepared、ContextInitialized、Prepared、ContextRefreshed、Started、runner、Ready 的顺序与每一步时 Bean 定义数;@Component 监听器收不到前四个事件;runner 抛异常和 Bean 初始化失败时的事件与退出码;内嵌 Tomcat 的端口在 runner 之前就开了,慢 runner 期间 readiness 是 REFUSING_TRAFFIC。
实测一个 409 个 Bean 定义的 Spring Boot 4 应用的启动时间:refresh 阶段占 main 到 Ready 的大头,最慢的 Bean 都是框架自己的;spring.main.lazy-initialization 让启动快 166 ms 但第 1 次请求从 41.7 ms 变成 123.0 ms;-XX:TieredStopAtLevel=1 略快,SerialGC 不快;AppCDS 动态归档把外部计时启动时间从 1083 ms 降到 759 ms。
同一份 Spring Boot 4.0.8 webmvc 应用换内嵌容器:Tomcat 与 Jetty 的启动、内存和快接口吞吐相差不大;两个 starter 同时在 classpath 时跑的是 Jetty;阻塞接口在默认 200 线程下 Tomcat 与 Jetty 都约 3600 请求/秒,打开虚拟线程后 Tomcat 到 6467 而 Jetty 要再调大 threads.max;请求头超限、// 与 %2e%2e 的路径、空闲连接超时两个容器反应不同;Boot 4.0.8 已经没有 Undertow。
实测 Spring Boot 4.0.8 的优雅停机:server.shutdown 的默认值就是 graceful,在途的 3 秒请求能完成,新连接立刻被拒绝;graceful 救不了新请求,负载均衡摘除延迟 2 秒的模拟里,不睡 149~162 个请求全部失败,preStop 睡 3 秒加上 graceful 才是零失败;阶段超时默认等 30 秒。
实测 Spring Boot 4.0.8 可执行 jar 的结构:Main-Class 是 JarLauncher、Start-Class 才是自己的主类,34 个嵌套 jar 以不压缩方式存放;胖 jar 比解压后的目录启动慢 150 到 190 ms;分层提取之后改一个类只有 5 KB 的 application 层指纹变化,19192 KB 的 dependencies 层不变;在胖 jar 里 ClassPathResource.getFile() 会失败而 getInputStream() 正常。