@repeatable 通过编译器自动封装为容器注解并增强反射语义实现重复标注,字节码中仅存容器注解,需用 getannotationsbytype() 获取间接存在的重复注解。

Java 的 @Repeatable 不是“绕过”注解限制,而是用编译器自动封装 + 反射语义增强的方式,让重复标注在写法上更自然、在运行时仍可统一处理。
底层本质:编译器帮你把多个注解塞进一个容器
Java 字节码规范规定:同一个元素(如类、方法)的 RuntimeVisibleAnnotations 属性里,**同类型注解只能出现一次**。所以 @Repeatable 并没有打破这条规则,而是由编译器完成三步转换:
- 检查该注解是否被
@Repeatable(Container.class)标注,并确认Container是合法容器(含value()方法,返回对应注解类型的数组) - 将所有重复出现的
@Author(...)实例,打包成一个@Authors({@Author(...), @Author(...)})容器注解 - 最终字节码中,目标元素只记录
@Authors这一个注解 ——@Author本身不会直接出现在注解表里
你可以用 javap -v YourClass.class 验证:看到的只有 @Authors,其 value 数组里才藏着两个 @Author 实例。
反射获取的关键:用对方法,否则拿不到
普通反射方法(如 getAnnotation(Author.class))只查“直接存在”的注解,而 @Author 在字节码里并不直接存在,所以会返回 null。真正能批量拿到重复注解的是以下两个方法:
-
getAnnotationsByType(Author.class):返回所有Author实例(包括“间接存在”的,即从@Authors.value()中提取的) -
getDeclaredAnnotationsByType(Author.class):同上,但不继承父类/接口上的重复注解
这两个方法会自动识别容器注解(如 @Authors),并把它的 value() 数组展开,合并父类中同类型的重复注解(若使用 ByType 版本),最终返回干净的 Author[] 数组。
容器注解不是摆设:它必须满足硬性约束
如果自定义重复注解,容器类写错,编译直接失败。核心要求有:
-
@Retention必须与重复注解完全一致(比如都是RUNTIME),否则反射无法还原 -
@Target要覆盖或等于重复注解的目标范围(例如@Author标在方法上,则@Authors的@Target至少也要包含METHOD) -
value()方法必须存在,且返回类型严格为Author[](不能是List<author></author>或其他包装) - 容器注解自身不能也被
@Repeatable标注(禁止嵌套容器)
为什么 getAnnotation() 拿不到,而 getAnnotationsByType() 可以
这是 Java 反射 API 对“存在形式”的明确定义:
-
directly present:字节码中
RuntimeVisibleAnnotations表里真实列出的注解 →@Authors属于这一类 -
indirectly present:通过容器注解的
value()数组间接关联的注解 →@Author属于这一类 -
getAnnotation()只查 directly present;getAnnotationsByType()则查所有 “associated” 注解(directly + indirectly + 父类 inherited)
换句话说:不是反射“漏了”,而是设计上就区分了“物理存在”和“逻辑存在”。你要的是逻辑语义上的多个 @Author,就得用匹配语义的方法。










