@repeatable 是编译期与运行时协同机制,需定义业务注解及严格合规的容器注解,运行时必须用 getannotationsbytype() 获取全部实例。

重复注解不是“多写几次”那么简单
Java 的 @Repeatable 不是语法糖,而是一套编译期与运行时协同工作的机制。它要求你同时定义两个角色:一个可被多次书写的业务注解(如 @Role),和一个配套的容器注解(如 @Roles)。字节码里只存容器,原始注解不直接出现——这意味着,如果只用传统反射方法,根本拿不到重复项。
容器注解必须严格合规,否则编译失败
自定义可重复注解时,容器不是随便起个名字加个数组就行。它必须满足硬性约束:
-
@Retention 必须与业务注解完全一致,比如都设为
RetentionPolicy.RUNTIME,否则运行时反射无法还原 -
@Target 范围不能比业务注解更窄,若
@Role允许标在方法和类上,@Roles就必须同时声明ElementType.METHOD和ElementType.TYPE - 必须有且仅有一个
public Role[] value()方法,类型必须是数组,不能是List<role></role>或Object[] - 容器自身不能被
@Repeatable标注,禁止嵌套容器
运行时必须用 getAnnotationsByType() 才能拿到全部
这是最容易踩坑的地方。以下方法行为完全不同:
-
getAnnotation(Role.class)→ 总是返回null,因为@Role在字节码中不“直接存在” -
getAnnotationsByType(Role.class)→ 正确方式,自动识别容器、展开value()数组、合并父类/接口上的同类型注解,返回完整Role[] -
getDeclaredAnnotationsByType(Role.class)→ 同上,但不继承,只取当前声明位置的实例 -
getAnnotation(Roles.class).value()→ 可用但冗余,需手动解包,且无法自动合并父类注解
框架集成建议:统一抽象,避免散落解包逻辑
在 Spring AOP、拦截器或注解驱动配置中,应把重复注解读取封装成标准工具方法:
- 所有切面逻辑基于
getAnnotationsByType(Xxx.class)统一入口,不直接调用容器 - 避免在多个地方重复写
if (ann instanceof Xxx) { ... }或手动遍历value() - 若需兼容 Java 7 风格容器(如
@Xxxs),可在工具层做适配,对外仍保持Xxx[]返回语义 - IDE 和 javac 编译结果为准,不要依赖编辑器提示是否准确
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











