构建可扩展注解系统关键在于@Target、@Retention、@Documented的精准协同:@Target须具体限定作用域,@Retention需匹配使用时机(如运行时逻辑必选RUNTIME),@Documented应作为公共注解的显性契约;慎用@Repeatable和@Inherited以避免隐式歧义。

构建可扩展的注解系统,关键不在堆砌功能,而在于元注解的精准组合与语义清晰。真正影响扩展性的,是 @Target、@Retention 和 @Documented 的协同设计,而非过度依赖 @Repeatable 或 @Inherited。
明确作用域:@Target 要具体,不宽泛
注解若能被误用在任意位置,就等于没有约束。比如标记“配置项”的注解,只应作用于字段;标记“策略入口”的注解,只应作用于类或方法。
- 避免写
@Target(ElementType.TYPE)就完事——它允许注解出现在类、接口、枚举、注解类型上,但实际可能只需用于@Service类 - 优先组合使用:
@Target({ElementType.TYPE, ElementType.METHOD})比单个TYPE更准确 - 若仅用于 Spring Bean 初始化后钩子,就限定为
ElementType.METHOD,并配合@Retention(RetentionPolicy.RUNTIME)
生命周期要匹配真实使用时机
注解是否需要反射读取?是否参与编译检查?是否要出现在生成的文档里?这些决定了 @Retention 策略,选错会导致注解“失效”。
- 运行时逻辑(如权限校验、动态路由)必须用
RetentionPolicy.RUNTIME - 仅用于编译期检查(如参数非空断言),选
SOURCE更轻量,不进 class 文件 -
CLASS很少用——既不参与编译检查,又无法反射获取,容易造成语义断层
@Documented 不是装饰,而是契约显性化
当注解本身承载业务含义(如 @AdminOnly、@Retryable),它就该成为 API 文档的一部分。不加 @Documented,Javadoc 就不会显示该注解,使用者无法感知其存在。
立即学习“Java免费学习笔记(深入)”;
- 所有面向调用方的公共注解,建议默认加上
@Documented - 内部框架使用的注解(如 Spring 的
@Bean实现类上的元注解),可省略 - 和
@Retention(RetentionPolicy.RUNTIME)配合,才能确保文档与运行时行为一致
慎用 @Repeatable 和 @Inherited
这两个元注解看似增强表达力,实则引入隐式行为,易引发歧义和维护成本。
-
@Repeatable要求定义容器注解,且需确保重复语义合理——比如@AliasFor可重复,但@Transactional在同一方法上重复毫无意义 -
@Inherited仅对CLASS有效,且只继承类上的注解,不作用于方法或字段;子类若自己加了同名注解,父类注解即被覆盖,不是“叠加” - 多数扩展场景靠显式扫描(如 Spring 的
AnnotationUtils)比依赖继承更可控


















