@repeatable注解支持同一方法上叠加多个权限注解,如@requirerole("admin") @requirerole("auditor"),运行时通过getannotationsbytype(requirerole.class)获取全部实例,便于统一校验角色与操作权限。

直接在方法上叠加多个权限注解,而不是包在数组里——这是 @Repeatable 的核心价值。它让“一个接口需满足多个岗位约束”这类需求,写起来自然、读起来清晰、维护起来不绕弯。
定义可重复的权限注解
先写业务注解,再配容器,两步缺一不可:
- 业务注解(如 @RequireRole)必须标注 @Repeatable(RequireRoles.class),并明确 @Target 和 @Retention(RetentionPolicy.RUNTIME)
- 容器注解(如 @RequireRoles)必须是 @interface,且只含一个 value() 方法,返回类型严格为 RequireRole[]
- 两者 @Target 范围要一致(比如都支持 METHOD 和 TYPE),@Retention 都得是 RUNTIME,否则运行时拿不到
在方法上连续标注多个岗位
定义好后,使用就回归直觉:
- 可以这样写:@RequireRole("ADMIN") @RequireRole("AUDITOR"),中间不加逗号、不套括号
- 编译器自动聚合成 @RequireRoles({@RequireRole("ADMIN"), @RequireRole("AUDITOR")}),但你完全不用写、也不用看到它
- 支持和其他注解混用,比如和 @PostMapping、@Transactional 并列,互不影响
运行时正确获取所有实例
反射读取方式错了,就等于白配:
- ✅ 正确方式:用 method.getAnnotationsByType(RequireRole.class),直接返回 Role[] 数组,包含所有叠加的注解
- ❌ 错误方式:用 method.getAnnotation(RequireRole.class),永远返回 null(字节码里根本没存单个 @RequireRole)
- ⚠️ 可用但冗余:先取容器 method.getAnnotation(RequireRoles.class),再调 .value() 解包——多一层,还无法自动合并父类上的同类型注解
配合拦截器统一做权限校验
拿到注解后,校验逻辑可以高度复用:
- 拦截器里遍历 getAnnotationsByType(RequireRole.class) 得到的每个实例
- 检查当前用户是否持有对应角色,任一不满足即拒绝,并用 reason() 字段返回具体提示
- 若注解还带 actions() 字段(如 {"EXPORT_LOG", "ALTER_CONFIG"}),可进一步校验操作行为是否越界
- 规则即配置,无需硬编码映射表,新增岗位红线只需加一行注解











