受检异常是java接口的强制性契约,要求调用方显式处理失败,其约束写入字节码且由javac强制执行;注解处理器可在编译期拦截未合规调用,与@nonnull等注解协同覆盖多维风险,但须避免滥用。
利用受检异常的强契约约束配合注解处理器,在编译期拦截核心代码,本质是把“方法可能因外部因素失败”这一语义固化进接口,并通过静态分析提前暴露违规调用。这不是运行时补救,而是从源头切断错误传播路径。
明确受检异常作为接口契约的核心作用
Java 中 IOException、SQLException 等受检异常一旦出现在方法签名中,就构成强制性契约:调用方必须显式处理——要么 try-catch,要么在自身 throws 中声明。这个约束被写入字节码的 Exceptions 属性,是 JVM 加载时可读的元信息,不是编译器临时判断。
- 它迫使开发者在写调用代码时就正视失败可能性,而非事后堆栈里找 NPE
- 它天然形成调用链上的“异常出口”责任传递,直到 main 或顶层 API 边界
- 与 @NonNull、@Nullable 等契约注解不同,受检异常的约束由 javac 强制执行,无需额外插件
用注解处理器识别并拦截未合规的调用点
注解处理器(如基于 javax.annotation.processing)可在编译期扫描源码,定位那些调用了声明受检异常的方法、却未做任何处理的代码行。例如:
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
- 标记关键资源操作类:@CriticalIO public class FileService { public void read(String path) throws IOException { ... } }
- 注解处理器遍历 AST,发现 new FileService().read("x.txt") 出现在无 try/throws 的方法中 → 触发编译错误或警告
- 可结合 Error Prone 或自定义 DiagnosticListener,输出类似 “Call to @CriticalIO method requires explicit exception handling”
与 Lombok / Spring 契约注解协同增强
单一机制有盲区,组合使用才能覆盖更广场景:
- 受检异常管“外部失败”,@NonNull 管“空值缺陷”,二者在编译期分别拦截不同维度风险
- 对非受检但高危的操作(如 JSON 反序列化),可用 @Throws(JsonParseException.class) 自定义注解 + 处理器模拟受检行为
- Spring 的 @NonNull/@Nullable 可配合编译器插件(如 IntelliJ 的 nullability inspection)实现跨模块契约推导
避免常见误用陷阱
强契约不等于滥用,否则会污染 API 清晰度:
- 不要为每个可能 throw new RuntimeException 的地方都包装成受检异常——这违背“可恢复性”设计原则
- 注解处理器不能替代 throws 声明本身;它只是辅助检查,不能绕过 javac 对受检异常的基本校验
- 若项目已大量使用非受检异常封装业务错误,应优先统一升级到 @ResponseStatus + 全局异常处理器,而非强行倒推加受检异常










