该问题本质是领域模型继承链中方法调用失控、状态耦合过深、协作契约断裂,导致无限委托、重复初始化、上下文错乱、线程安全崩塌;解决路径包括:明确调用契约、用组合替代深层继承、控制状态生命周期、加入轻量级防护与可观测锚点。

这个问题本质不是“引擎罢工”,而是领域模型继承链中因方法调用路径失控、状态耦合过深、协作契约断裂,导致运行时行为不可预测——比如无限委托、重复初始化、上下文错乱、线程安全崩塌,最终表现为引擎卡死、响应停滞或直接抛出 StackOverflowError / ConcurrentModificationException / IllegalStateException。
关键不在“高频”,而在“交叉调用”缺乏边界与约定。下面从四个实操维度给出解决路径:
明确调用契约,禁用隐式委托
在充血模型或多层继承(如 Order → TradeOrder → SettlementOrder)中,常见错误是子类方法里无意识调用 super.xxx(),而父类又反过来调用子类重写的钩子(如 onValidate()),形成闭环。
-
✅ 强制所有跨层级调用走显式、命名清晰的协作接口,例如:
// 不要这样(隐式、易断链) protected void validate() { super.validate(); doCustomCheck(); } // 改为这样(契约化、可审计) protected final void validate() { preValidate(); // 父类定义的前置检查点 doCustomValidation(); // 子类必须实现,但不得反向调用validate() postValidate(); // 父类定义的后置收尾 } ❌ 禁止在
@PostConstruct、@EventListener或回调方法中调用super或触发子类重写逻辑。
拆解“交叉”场景,用组合替代深层继承
当 A → B → C 之间存在双向依赖(如 C 要调用 B 的计算服务,B 又依赖 C 的策略实例),说明继承已超出表达“is-a”关系的能力,本质是职责混杂。
- ✅ 将共用能力抽为独立服务或策略组件:
- 把校验逻辑移到
ValidationEngine,由各模型按需注入; - 把状态流转委托给
StateTransitionService,而非靠super.transition()逐层传递; - 使用
@Delegate(Lombok)或手动委派,让TradeOrder持有SettlementPolicy实例,而不是继承它。
- 把校验逻辑移到
- ✅ 对必须保留的继承链,加
final修饰关键协调方法(如execute()),只开放template method钩子。
控制状态生命周期,避免跨实例污染
高频调用下最隐蔽的坑是:同一个领域对象被多个线程/事务反复复用,或在未清理状态下被二次初始化。
- ✅ 所有含状态的领域对象(尤其带
Map、List缓存字段的)做到:- 初始化即不可变(
private final+ 构造器注入); - 可变状态封装在
@Transient辅助对象中,每次业务操作新建; - 若需复用,用工厂模式生成新实例,而非
.reset()或清空字段。
- 初始化即不可变(
- ✅ 在 Spring 环境中,避免
@Scope("prototype")的领域对象被@Autowired注入到单例 service 中——这会导致状态泄漏。
加入轻量级防护与可观测锚点
不靠日志堆砌,而是在关键交汇点埋设防御性检查:
- 在每个可能被交叉调用的方法入口加:
if (Thread.currentThread().getStackTrace().length > 20) { throw new IllegalStateException("Suspected recursive call chain detected"); } - 为每个领域对象生成唯一 traceId(如
UUID.randomUUID().toString().substring(0,8)),在关键方法参数/返回值中透传,并记录className + method + traceId + depth; - 使用 Micrometer 或自定义指标统计:
-
domain.method.call.depth.max(调用栈深度峰值) -
domain.object.reused.count(同一实例被多少次业务流程复用)
-
这类问题不复杂但容易忽略,核心是把“谁调用谁”变成可声明、可验证、可截断的契约,而不是靠运气跑通。

















