@Repeatable是需严格配对容器注解的机制,解决语义冗余与反射读取不便;适用多角色权限、多校验规则、多路由路径等并列同类型语义场景。

Java 8 的 @Repeatable 不是让注解“随便多写几次”就能生效的语法糖,而是一套需严格配对、主动设计的机制——它真正解决的是语义表达冗余和反射读取不便的问题。用对了,代码更直觉;用错了,编译报错或运行时拿不到数据。
哪些场景适合用 @Repeatable
核心判断标准:同一目标元素(如方法、类)需要表达多个独立但同类型的语义单元,且这些单元在逻辑上是并列、可枚举的。
- 权限控制:一个接口需 ADMIN + EDITOR + VIEWER 多角色访问,而不是“一个角色包含所有权限”
-
校验规则:对同一字段叠加多个约束,如
@NotBlank、@Size(max=20)、@Pattern(regexp="^U[0-9]{5}$")(注意:JDK 自带注解不可重复,需自定义) -
路由映射:一个处理器支持多个路径,如
@Path("/users")@Path("/api/v1/users") - 审计标记:记录多个评审人、多个修改时间点、多个业务阶段标识
必须配对容器注解,缺一不可
单独加 @Repeatable(XXX.class) 到业务注解上,不定义容器注解,代码直接编译失败。
- 容器注解的
@Target必须覆盖业务注解的所有使用位置(比如业务注解标在 METHOD 上,容器也必须声明@Target(ElementType.METHOD)) - 容器注解的
@Retention必须与业务注解一致,且必须是RUNTIME才能在运行时通过反射获取 - 容器中必须且只能有一个名为
value()的抽象方法,返回类型严格为业务注解[](例如Role[] value();),不能是List<Role>或Role - 容器注解自身不能加
@Repeatable,也不允许嵌套其他容器
写法自然,但运行时获取有讲究
定义完成后,使用非常直观:
立即学习“Java免费学习笔记(深入)”;
@Role("ADMIN")
@Role("EDITOR")
@Role("VIEWER")
public void updateUser() { }
但反射读取时,以下方式无效:
-
method.getAnnotation(Role.class)→ 总是返回null或未定义行为(取决于 JVM 实现) -
method.getAnnotations()→ 只返回容器注解实例(如@Roles),不会展开内部数组
正确方式只有一种:
-
Role[] roles = method.getAnnotationsByType(Role.class);→ 直接拿到全部三个实例,无需解包
如果旧代码已依赖容器注解,也可用 method.getAnnotation(Roles.class).value(),但这属于兼容过渡,不推荐新逻辑采用。
常见踩坑点
- 容器注解漏写
@Retention(RetentionPolicy.RUNTIME)→ 运行时反射完全拿不到任何内容 - 把
@Repeatable(Roles.class)错写成@Repeatable(Role.class)或字符串"Roles"→ 编译报错 - 容器的
value()方法返回Collection<Role>或Role单例 → 编译不通过 - 误以为
@Override、@Deprecated等 JDK 内置注解也能重复 → 它们未标注@Repeatable,语法上禁止重复


















