@target是编译期硬性约束,非运行时检查;必须用数组语法显式指定elementtype,如@target({elementtype.method}),缺省或错配将导致编译失败,且须与@retention(runtime)协同才能反射读取。

关键在 @Target —— 它不是运行时检查,而是编译期的硬性约束。写错位置,javac 直接报错,连 class 文件都不会生成。
用 @Target 明确声明允许的位置
不加 @Target,注解默认只能用于其他注解(ElementType.ANNOTATION_TYPE),几乎等于废掉。必须显式指定 ElementType 枚举值,且必须用大括号包成数组:
- 只允许标在方法上:
@Target({ElementType.METHOD}) - 只允许标在类、接口、枚举上:
@Target({ElementType.TYPE}) - 只允许标在字段(成员变量)上:
@Target({ElementType.FIELD}) - 只允许标在方法参数上:
@Target({ElementType.PARAMETER}) - 同时支持方法和类:
@Target({ElementType.METHOD, ElementType.TYPE})
别堆砌,按真实用途最小化授权
一个注解该支持哪些位置,取决于它要解决的问题。盲目加一堆 ElementType,反而增加误用风险:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 权限校验类(如 @RequiresRole):通常只需 METHOD,加 TYPE 可能导致整类被统一拦截,失去控制粒度
- 字段校验类(如 @NotBlank):至少得覆盖 FIELD 和 PARAMETER,否则无法校验入参
- 配置标识类(如 @Configuration):只用 TYPE 就够了
- 泛型约束类(如 @NonNull):必须显式加上 TYPE_USE 或 TYPE_PARAMETER,否则 List 这种写法无效
必须搭配 @Retention(RetentionPolicy.RUNTIME)
@Target 控制“能不能贴”,@Retention 控制“贴完还在不在”。两者缺一不可:
- 只加 @Target,没加 @Retention(RUNTIME):编译能过,但反射 getAnnotation() 拿不到,框架也扫描不到
- 加了 @Retention(RUNTIME),但 @Target 漏了实际使用位置:编译直接失败,比如定义为 METHOD 却贴在字段上,报错提示 “not applicable to this kind of declaration”
- 常见错误组合:@Target({ElementType.LOCAL_VARIABLE}) + @Retention(RUNTIME) —— JVM 禁止运行时读取局部变量注解,反射始终返回 null
IDE 和构建工具会帮你提前发现
IntelliJ、Eclipse 或命令行 javac 都会在编辑或编译阶段实时提示错误位置。例如:
- 把本该用于字段的 @Loggable 标在方法参数上 → IDE 标红,提示 “Annotation is not applicable to this location”
- Gradle/Maven 构建时严格校验,比 IDE 更可靠;尤其 Kotlin/Java 混合项目中,IDE 可能宽松,但 maven-compiler-plugin 一定拦住
- 自定义注解若未设 @Target,团队协作时全靠人盯,容易遗漏;写进元数据里,等于把规范嵌进代码本身
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










