应使用@target({elementtype.method, elementtype.type})配合@retention(retentionpolicy.runtime)限定注解仅用于方法或类型,排除local_variable以在编译期硬拦截误用,再结合运行时aop校验实现双重防护。

直接用 @Target(ElementType.METHOD) 或 @Target({ElementType.TYPE, ElementType.METHOD}),并配合 @Retention(RetentionPolicy.RUNTIME) —— 这样一来,只要有人把注解贴在局部变量上,javac 在编译阶段就立刻报错,根本进不了运行时。
只允许贴在方法或类上,彻底封死局部变量
局部变量对应的是 ElementType.LOCAL_VARIABLE。如果你的权限注解本意是控制接口访问(比如 @RequirePermission),那它就不该出现在方法体内部的 int x = 1; 前面。定义注解时明确排除这个位置:
- 写
@Target({METHOD, TYPE}):仅允许标注在方法声明或类/接口上 - 不写
LOCAL_VARIABLE,也不留空(漏写@Target默认只允许标注其他注解,但容易被忽略) - 一旦开发者尝试这样写:
@RequirePermission("user:read")
String token = request.getHeader("auth");
编译器直接提示:annotation @RequirePermission is not applicable to this kind of declaration
别依赖 IDE 提醒,靠编译器硬拦截
IntelliJ 有时对 @Target 检查较宽松,尤其在 Kotlin/Java 混合项目中可能“看起来不报错”。但 Maven 的 maven-compiler-plugin 会严格执行。CI 流水线失败往往就卡在这儿——不是逻辑错,是注解挂错了地方。所以:
- 本地开发时也启用 Maven 编译(
mvn compile),别只靠 IDE 自动构建 - 在 CI 脚本中加入
-Werror参数,把警告当错误处理,避免漏掉隐性问题 - 如果团队用 Lombok,注意它的
@Data等注解可能生成额外方法,也要确认你的权限注解不会意外被继承或误标
配合运行时校验,双重保险
光有 @Target 能防编译期误用,但不能防反射绕过或动态代理场景。建议在权限校验逻辑里再加一层检查:
- 在 AOP 切面中,通过
joinPoint.getSignature()确认当前拦截的是方法,不是字段或局部变量 - 调用
method.getAnnotation(RequirePermission.class)前,先断言method != null,避免因注解位置错误导致空指针 - 日志中记录注解实际读取位置,例如:
found @RequirePermission on method 'updateBalance' (not on local var),便于审计和排查











