非受检异常不参与编译检查且不能隐式传播上下文,真正起作用的是类加载器隔离配合ScopedValue或ThreadLocal的显式绑定;类加载器隔离实现模块独立类视图与版本混用;虚拟线程下需用ScopedValue替代ThreadLocal传递上下文;异常仅用于校验与熔断,不可承载业务字段。
非受检异常本身不参与编译期检查,不能直接“隐式传播”上下文信息。真正起作用的是类加载器隔离配合 threadlocal 或 scopedvalue 的显式绑定策略,而非依赖异常机制。所谓“利用非受检异常隐式传播”,是一种常见误解——异常是控制流中断手段,不是上下文载体。
类加载器隔离才是脚手架低耦合的核心基础
脚手架要支持多业务模块并存、版本混用、热插拔等能力,关键在于让每个模块拥有独立的类视图。这靠的是自定义类加载器打破双亲委派:
- 每个插件或业务包使用专属
URLClassLoader或重写的SecureClassLoader加载,互不可见 - 核心框架类(如
CommonService接口)由父加载器提供,确保契约统一 - 冲突类(如不同版本的
fastjson)被各自加载器隔离,不会触发NoClassDefFoundError或LinkageError
上下文传递必须绕过 InheritableThreadLocal 的失效陷阱
Spring Boot 脚手架若启用虚拟线程(Java 21+),默认的 MDC.put() 或 ThreadLocal.set() 在子任务中会丢失。这不是异常问题,而是虚拟线程设计使然:
- 改用
ScopedValue.where(KEY, value).run(() -> scope.fork(...))显式绑定 - 避免在
fork()外部设置上下文,否则子任务无法继承 - 若需兼容老代码,可通过
-Djdk.virtualThreadScheduler.parallelism=0临时退回到平台线程调试
异常仅用于边界控制,不承担数据传递职责
在脚手架中,非受检异常(如 IllegalArgumentException、IllegalStateException)应严格用于流程校验和失败熔断:
- 插件加载失败时抛出
PluginLoadException(运行时异常),由统一异常处理器捕获并记录隔离上下文 - 不把 traceId、tenantId 等业务字段塞进异常构造参数来“传递”,那会导致堆栈污染且不可靠
- 真正的上下文透传走
ScopedValue+ 作用域生命周期管理,与异常无关
脚手架的低损耗来自结构清晰、加载可控、传播明确。靠异常“隐式”做事,只会让逻辑变模糊、排查变困难。










