要让自定义注解支持重复使用,必须同时满足声明可重复和运行时可取用:业务注解需标注@repeatable并指向合规容器,容器注解须为@interface、仅含value()方法且返回类型为业务注解数组,@target和@retention须与业务注解一致;使用时直接叠加书写,反射须用getannotationsbytype()获取全部实例。

要让自定义注解支持重复使用,不是加个 @Repeatable 就完事——它必须和容器注解协同工作,编译器、字节码和反射三者共同配合才能生效。核心在于“声明可重复”和“运行时可取用”两个环节都必须正确。
定义可重复注解本身
业务注解需明确指向其容器,并满足元注解一致性要求:
- 必须标注
@Repeatable(YourContainer.class),且YourContainer类型必须已正确定义、可见、无拼写或包路径错误 -
@Target和@Retention必须显式声明,且值与容器注解完全一致(例如都为ElementType.METHOD和RetentionPolicy.RUNTIME) - 允许有任意数量的属性,但所有属性都应提供默认值(避免使用时强制填写)
编写合规的容器注解
容器不是命名习惯问题,而是编译器强校验对象:
- 必须是
@interface类型,不能是普通类或接口 - 必须仅声明一个名为
value()的方法,返回类型严格为你的注解[](如Role[] value();) - 不能有其他非默认元素;若添加额外字段,必须设默认值,否则编译失败
-
@Target范围不能比业务注解窄(例如业务注解支持METHOD和TYPE,容器也必须同时声明这两个)
在代码中自然叠加使用
语法上无需任何包装,直接多次书写即可:
- 支持在同一方法、类、字段等目标位置连续写多个相同注解,如:
@IdempotentStrategy(key = "#id")@IdempotentStrategy(key = "#orderNo", expireSeconds = 3600) - 编译后字节码中只保留容器注解实例(如
@IdempotentStrategies),原始注解不单独存在 - 禁止混用:不能既写多个
@Xxx,又手动写一次@Xxxs({@Xxx, @Xxx}),否则编译报错
运行时统一获取全部实例
反射读取方式决定能否真正拿到所有声明:
- ✅ 正确方式:
method.getAnnotationsByType(Xxx.class)—— 自动展开容器、合并继承注解、返回完整数组 - ❌ 错误方式:
method.getAnnotation(Xxx.class)—— 始终返回null(因字节码中无直接注解) - ⚠️ 可用但冗余:
method.getAnnotation(Xxxs.class).value()—— 需多一层解包,且无法自动合并父类上的同类型注解











