元注解是权限注解生效的底层前提:@retention(runtime)确保反射可读取,@target(method)精准限定作用位置,@documented和@inherited在权限场景中非必需;缺runtime则鉴权链断裂,错加无关elementtype会引发语义污染。

元注解不是权限逻辑本身,而是让权限注解能被正确识别、传递和执行的底层规则。在企业级权限框架中,真正起作用的不是@RequiresPermission("user:delete")这行字,而是它背后那几个看似简单的@Target和@Retention——它们决定了这个权限标签能不能被AOP切面拿到、能不能在运行时生效、会不会被子类继承错。
@Retention(RetentionPolicy.RUNTIME) 是运行时鉴权的前提
权限校验必须发生在方法真正执行前,这就要求注解信息完整保留在JVM运行期。如果漏写@Retention,或误用CLASS甚至SOURCE,反射调用method.getAnnotation(RequiresPermission.class)永远返回null。Spring的@Transactional、@PreAuthorize全部依赖RUNTIME生命周期,你的自定义权限注解也一样——没有它,整个鉴权链从第一步就断了。
@Target 精准限定作用位置,避免语义污染
权限控制绝大多数落在方法上,所以@Target(ElementType.METHOD)是标配。若需支持类级别统一约束(如整个@RestController下所有接口都需登录),可扩展为@Target({ElementType.METHOD, ElementType.TYPE})。但要避免盲目添加CONSTRUCTOR、FIELD或LOCAL_VARIABLE:权限逻辑不作用于字段赋值或局部变量,加了不仅无用,还可能误导后续维护者。
@Documented 和 @Inherited 在权限场景中多数可省略
@Documented只影响Javadoc生成,不影响任何运行时行为;而@Inherited对方法注解完全无效——JVM规范明确说明,该元注解仅对类级注解生效,子类重写方法不会自动继承父类方法上的权限注解。真需要“继承式”权限,应由AOP切面统一处理类+方法双层注解组合,而不是依赖元注解机制。
反射读取注解时的关键避坑点
即使元注解写对了,反射仍可能拿不到注解,常见原因有三个:
- 代理对象干扰:Spring AOP使用CGLIB或JDK动态代理后,直接对代理类调用
getMethod()可能返回错误方法。应优先使用HandlerMethod.getMethod()(Spring MVC)或joinPoint.getSignature().getMethod()(AOP)获取原始目标方法 - private方法限制:
getMethod()只返回public方法,private权限注解需改用getDeclaredMethod()并配合setAccessible(true) - 注解未被扫描进上下文:若自定义注解未被Spring组件扫描器识别(比如没配
@ComponentScan或未启用AOP),切面根本不会触发,注解形同虚设
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











