近日,一则关于Spring框架新模块的讨论在开发者社区引发轩然大波。有技术博主在社交平台发文指出,Spring最新发布的某实验性模块在默认配置下竟“完全不支持声明式事务”,这一说法迅速引爆技术圈。毕竟,事务管理是Spring框架的核心能力之一,从早期XML配置到如今的@Transactional注解,事务支持一直是企业级应用的基石。若新模块真与事务“绝缘”,无疑是对Spring生态的一次重大冲击。
争议起源:一个被误读的“特性”
引发争议的模块是Spring团队于本月初推出的 Spring Modulith 2.0(模块化单体架构支持库)。该模块旨在帮助开发者将单体应用拆分为逻辑模块,同时保留单体部署的简洁性。然而,有用户在使用中发现,当在模块内部方法上添加@Transactional注解时,事务并未按预期生效——跨模块调用时,异常回滚竟然失效了。
“我们测试了最简单的转账场景,A模块调用B模块的方法,B方法抛出运行时异常,A模块之前的数据修改没有回滚。”一位署名“codeReviewer”的开发者在其技术博客中写道。该博文迅速获得上千次转发,不少开发者附和称“遇到了同样的问题”。
官方回应:并非缺陷,而是设计选择
面对汹涌的舆论,Spring团队在GitHub issues中火速回应。项目负责人Oliver Drotbohm解释道:Spring Modulith 2.0默认采用“无事务边界”模式,这是有意为之的设计决策,而非Bug。
“在模块化架构中,每个模块应被视为独立的自治单元。如果跨模块调用默认开启事务,会导致模块间的强耦合,破坏模块独立性。”Oliver在回复中强调,Spring Modulith鼓励开发者通过事件驱动或显式API网关进行模块间通信,而非直接共享事务上下文。他补充说,开发者仍可通过配置 @Transactional(propagation = Propagation.REQUIRED) 在单个模块内使用本地事务,但跨模块事务需手动启用“全局事务协调器”(如JTA或Seata)。
技术支持:分布式事务的妥协与进步
这一设计背后,折射出微服务与模块化架构演进中的深层矛盾。传统单体应用依赖数据库本地事务,能够轻松保证ACID。而模块化拆分后,每个模块可能拥有独立数据源,跨模块操作天然面临分布式事务难题。
Spring Modulith默认关闭跨模块事务,本质上是为了避免开发者陷入“伪分布式事务”陷阱——很多团队以为加了@Transactional就能实现跨库回滚,实际上大多数场景仅靠单一数据库事务无法做到。Spring选择将这一复杂性显性化:要么你明确使用XA协议或补偿事务框架(如Saga),要么接受模块间最终一致性。
行业反响:支持与批评并存
该设计立即引发两极评价。部分架构师表示赞赏:“Spring终于不再默认隐藏分布式事务的复杂性,这反而能倒逼团队认真设计模块边界。”而更多一线开发者则认为“违背直觉”:“我们习惯了@Transactional的便利,现在告诉我新模块默认不支持,升级成本太高。”
知名Spring技术专家Josh Long在最新一期播客中打圆场:“这不是一个Bug,而是一个特性——它迫使我们思考:真正需要事务的地方,是否应该把模块合并为一个业务单元?”与此同时,国内技术社区也出现大量教程,指导如何在Spring Modulith中手动启用事务,但多数教程最后都附上警告:“除非你清楚分布式回滚的代价,否则不要轻易使用。”
后续跟进:Spring计划提供“兼容模式”
迫于社区压力,Spring团队已承诺在下一个里程碑版本中提供“传统事务兼容模式”,允许开发者通过简单配置恢复旧有行为。但Oliver强调,该模式将被标记为“已废弃”,正如当年Spring Cloud对Hystrix的处理——给迁移留出缓冲期,但最终方向不可逆转。
对于广大Spring用户而言,这一事件无疑是一个重要信号:在云原生与模块化时代,事务不再是“开箱即用”的默认配置,而需要开发者根据业务场景做出明智权衡。 对于正在或计划采用Spring Modulith的团队,建议在评估业务一致性需求后,优先考虑事件溯源或Saga模式,而非简单依赖声明式事务。
毕竟,在软件架构的世界里,没有银弹——只有清醒的设计选择。