@repeatable注解必须配对定义容器注解,容器需满足:同@retention与@target、value()为被注解类型的数组;运行时须用getannotationsbytype()获取,而非getannotation()。

Repeatable 注解必须配对定义容器注解
Java 的 @Repeatable 不是开箱即用的“多贴标签”功能,它要求你**显式声明一个容器注解类型**,否则编译器直接报错 java.lang.annotation.Repeatable: Not a repeatable annotation type。这个容器注解必须满足两个硬性条件:一是本身必须标注 @Retention 和 @Target(通常与被重复的注解一致),二是其 value() 成员必须是数组类型,且元素类型为你要重复的那个注解类。
比如你想让 @Role 可重复,就得额外写一个 @Roles 容器:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Roles.class) // 指向容器
public @interface Role {
String value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Roles {
Role[] value(); // 必须叫 value(),且是 Role 数组
}
使用时语法和反射获取方式都不同
一旦定义好,使用上看似简单——直接在同一个地方写多个 @Role 即可。但背后机制变了:编译器会自动把它们打包进一个 @Roles 实例里。这意味着你在运行时不能用 getAnnotation(Role.class) 拿到单个实例,而必须用 getAnnotationsByType(Role.class) ——这是 Java 8 新增的方法,专为可重复注解设计。
-
getAnnotation(Role.class)返回null(因为源码里实际只存在一个@Roles) -
getAnnotationsByType(Role.class)才能正确返回所有@Role实例组成的数组 - 如果只用
getAnnotation(Roles.class),拿到的是容器对象,再调用.value()才能展开
注意注解保留策略和目标范围必须严格一致
容器注解和被重复注解的 @Retention 和 @Target 值必须完全相同,哪怕差一个 ElementType.METHOD 都会导致编译失败。常见错误是忘记给容器注解加 @Target,或者误设成 RetentionPolicy.CLASS(这时反射拿不到,getAnnotationsByType 返回空数组)。
- 两者
@Retention必须同为RUNTIME(若需反射读取)或同为CLASS/SOURCE -
@Target列表必须逐项相等,顺序无关但集合内容不能多也不能少 - IDE 有时不报错但编译器会拒绝,建议用
javac直接验证
嵌套泛型或复杂属性时容易触发类型擦除问题
如果 @Role 里有泛型参数(如 <t></t>)或非基本类型数组字段,@Repeatable 容器的 value() 成员可能无法正确承载。Java 注解本身不支持泛型,所有泛型信息在编译后被擦除,导致容器注解的数组类型和实际注入的注解实例类型不匹配。
更现实的坑是:当注解属性含 List<string></string> 或自定义枚举数组时,务必确认该类型能在注解中合法使用(注解只允许基本类型、String、Class、枚举、其他注解、以及上述类型的数组)。否则即使语法通过,运行时反射读取会抛 AnnotationFormatError。
真正难调试的地方往往不在定义,而在反射读取逻辑里漏掉了 ByType 后缀,或者容器注解的 value() 方法签名写错了返回类型——这种错误不会在编译时报红,但运行时永远拿不到数据。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











