@Repeatable 通过编译器自动将多个目标注解封装为容器注解,并要求容器类必须声明 public 目标[] value()、@Retention 与目标一致、@Target 不窄于目标;运行时须用 getAnnotationsByType() 才能获取全部重复注解。

Java 中的 @Repeatable 不是让注解“绕过限制”,而是通过编译器自动封装 + 反射语义增强,实现语法简洁、运行时可控的重复标注。它依赖一套明确的结构契约,缺一不可。
定义可重复注解的两步硬性结构
必须同时完成以下两件事,否则编译失败或运行时无法识别:
- 在目标注解(如
@Role)上标注@Repeatable(Roles.class),明确指向容器注解类 - 定义容器注解(如
@Roles),且满足三个强制要求:- 声明
public Role[] value()方法(类型必须严格为数组,不可用List或Object[]) -
@Retention策略与目标注解完全一致(例如都是RetentionPolicy.RUNTIME) -
@Target范围不能比目标注解更窄(若@Role支持TYPE和METHOD,则@Roles必须至少也包含这两个)
- 声明
编译期:字节码中只存容器,不存重复项
你写多个 @Role("A") @Role("B"),javac 会自动转换为单个容器实例:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 等价于手动写了
@Roles({@Role("A"), @Role("B")}) - 反编译 class 文件(
javap -v)只能看到@Roles注解,@Role不会单独出现在字节码的RuntimeVisibleAnnotations表中 - 这是 JVM 规范强制要求:同一位置同类型注解最多只允许存在一次
运行时:必须用 getAnnotationsByType() 才能拿到全部
普通反射方法无法还原重复语义,因为原始注解在字节码中并不“直接存在”:
-
getAnnotation(Role.class)→ 总是返回null(查的是“直接存在”的注解) -
getAnnotationsByType(Role.class)→ 自动识别@Roles容器,展开value()数组,并合并父类/接口上的同类注解,返回完整的Role[] -
getDeclaredAnnotationsByType(Role.class)→ 行为同上,但不向上继承,仅限当前声明位置 -
getAnnotation(Roles.class)虽可获取容器,但需手动调.value(),且无法自动处理继承链,不推荐作为主路径
常见错误与规避要点
这些细节看似小,但错一个就会导致编译失败或运行时拿不到数据:
- 容器注解忘了加
@Retention(RUNTIME)→ 反射读取为空 -
value()方法名写错(如写成roles()),或返回类型不是Role[]→ 编译报错 - 容器注解的
@Target漏了某个 ElementType(比如只写了TYPE,但想用在方法上)→ 编译报错 - 误用
getAnnotations()遍历 → 只能得到@Roles实例,看不到里面的@Role
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










