自定义注解需从设计意图、使用边界、解析机制三方面建立约束标准,明确存在理由、元数据契约、属性防错及工具链检查,杜绝元数据污染。

团队自定义注解不是“能写就行”,而是要从设计意图、使用边界、解析机制三方面建立可落地的约束标准。否则容易演变成元数据污染——到处是注解,却没人知道它何时生效、为何失效、改了会怎样。
明确注解的唯一存在理由
每个自定义注解必须回答三个问题:它替代了哪段重复代码?谁负责解析它?不加它会导致什么具体问题?
- 禁止为“看起来更高级”而加注解,比如用
@Log替代一行log.info(),除非配套有统一日志拦截器且已接入监控告警 - 业务语义类注解(如
@PayStrategy)必须绑定具体执行策略接口,不能只声明不实现 - 配置类注解(如
@Retry(times = 3))需配套默认行为,并在文档中写明“未标注时按全局重试策略执行”
用元注解锁死作用范围和生命周期
不加@Target和@Retention的注解等于没有契约。团队规约应强制这两项不可省略:
-
@Target至少指定两个以上目标,例如@Target({ElementType.METHOD, ElementType.TYPE}),避免误标在字段或参数上 -
@Retention(RetentionPolicy.RUNTIME)仅用于需反射读取的场景;若只是编译期校验(如API版本控制),用CLASS更安全 - 禁止使用
@Inherited除非确认子类必须继承该元数据——多数日志、权限、缓存注解都不需要继承
属性设计必须防错、可验证、不耦合
注解属性不是配置文件,不能承载运行时依赖或复杂逻辑:
立即学习“Java免费学习笔记(深入)”;
- 禁用
Class类型属性直接引用业务类,改用String类名 + 运行时Class.forName()加载,并包裹异常处理 - 枚举值优先于字符串,例如
CacheType.REDIS比cacheType = "redis"更安全 - 所有属性必须设默认值(
default ""或default NONE),避免调用方因遗漏导致编译失败或空指针 - 禁止属性间循环引用,两个注解不能互为对方的属性类型,否则编译报
cyclic annotation element type
检查工具链必须覆盖开发全周期
光靠文档和CR拦不住滥用,要把规则编进工具链:
- Checkstyle 自定义规则拦截语法违规:比如检测
@interface定义中是否缺失@Target、是否用了被禁用的属性类型 - PMD XPath 规则扫描注解使用位置:例如禁止在私有方法上使用
@AuthRequired,或要求@Transactional必须出现在public方法 - CI 流水线中启用
javac -Xlint:all,捕获hiding field等潜在冲突,并配合 Git hook 拦截提交 - IDEA 中配置 Annotation Processors 自动触发,确保新增注解在保存时即校验合法性


















