自定义权限注解生效的关键是:必须用@retention(retentionpolicy.runtime)确保运行时可见,@target(elementtype.method)精准作用于方法,配合aop反射正确获取目标方法并校验权限码。

要让自定义权限注解真正起作用,关键不在“写注解”,而在元注解选得准、反射调得稳、校验链走得通。核心就三点:注解必须在运行时可见、必须能精准挂到方法上、必须能被统一拦截并解析。
必须用 @Retention(RetentionPolicy.RUNTIME)
如果只写 @Retention(RetentionPolicy.CLASS) 或干脆不写(默认就是 CLASS),编译后注解就消失了,反射调 method.getAnnotation(RequiresPermission.class) 一定返回 null。Spring 的 @Controller、@Service 也是靠 RUNTIME 才能在启动时扫描注册 Bean。所以你的权限注解第一行就得是:
-
@Retention(RetentionPolicy.RUNTIME)—— 没这句,后面全白搭 - 别信 SOURCE 或 CLASS,它们连 JVM 加载后都拿不到,更别说运行时鉴权了
@Target 要精确匹配使用场景
权限控制绝大多数时候是方法级的,比如某个 API 接口或服务方法需要校验。那就锁定 ElementType.METHOD:
-
@Target(ElementType.METHOD)最常用也最安全 - 如果还想支持类级别统一开关(如整个 Controller 都需某权限),可加
TYPE:@Target({METHOD, TYPE}) - 避免盲目加
CONSTRUCTOR或FIELD—— 权限校验逻辑通常不作用于构造器或字段
别乱加 @Documented 和 @Inherited
这两个元注解对权限校验没实际价值,反而容易引入误解:
-
@Documented只影响 Javadoc 生成,和运行时逻辑无关,删掉不影响功能 -
@Inherited对方法注解完全无效(JVM 规范明确说明),子类重写方法不会继承父类方法上的注解,加了也没用 - 真正需要“继承性”的场景,应该靠 AOP 切面统一处理类 + 方法双层注解,而不是依赖 @Inherited
反射获取注解时绕不开的三个坑
写了注解、配对了元注解,不代表反射就能顺利用上。常见断点在这三处:
-
代理对象问题:Spring AOP 生成的 CGLIB 代理类,
method.getDeclaringClass()返回的是原始类,但实际执行的是代理类。应优先用handlerMethod.getMethod()(Spring MVC)或joinPoint.getSignature().getMethod()(AOP)直接拿到被调用的方法对象 -
private 方法限制:
getMethod()只查 public 方法,private 必须用getDeclaredMethod()+setAccessible(true),但生产环境常被 SecurityManager 拦截,建议权限注解只用于 public 方法 -
参数类型严格匹配:
String.class和int.class是不同签名,反射找方法时若参数类型不一致会抛NoSuchMethodException。定义注解时尽量避免依赖具体方法签名做校验,而是聚焦在权限码字符串本身
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











