因为重复注解在编译后被包装进容器注解,getannotation()仅设计用于单实例注解,只返回首个或null;而getannotationsbytype()会自动解包容器并聚合所有实例,是唯一标准方式。

Java 8 引入了重复注解(Repeatable Annotations),允许在同一个元素上多次声明同一类型的注解。但反射获取时不能直接用 getAnnotation(Class),必须使用 getAnnotationsByType(Class) 才能正确收集所有重复实例。
为什么 getAnnotation() 拿不到重复注解?
getAnnotation(Class<t>)</t> 只返回单个注解对象(或 null),它设计初衷就是针对“非重复”的传统注解。即使你写了多个 @MyAnno,它也只取第一个,或干脆返回 null(取决于底层实现,但行为不可靠)。
重复注解在编译后会被包装进一个容器注解(container annotation),而 JVM 运行时通过 getAnnotationsByType() 自动解包、聚合并返回所有实际声明的注解实例——这是唯一标准且可移植的方式。
正确使用 getAnnotationsByType() 的前提
要让 getAnnotationsByType() 正常工作,必须满足两个条件:
- 目标注解类上必须标注
@Repeatable(Container.class),且Container是一个合法的容器注解(保留策略为RetentionPolicy.RUNTIME,且其 value 成员类型为TargetAnno[]) - 容器注解本身也必须用
@Retention(RetentionPolicy.RUNTIME)声明,否则运行时反射无法读取
典型代码示例
假设定义了如下重复注解:
@Repeatable(Roles.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Role {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Roles {
Role[] value();
}
在类上这样使用:
@Role("ADMIN")
@Role("USER")
public class UserService { }
反射获取方式:
Role[] roles = UserService.class.getAnnotationsByType(Role.class);
// ✅ 返回长度为 2 的数组:[Role("ADMIN"), Role("USER")]
// ❌ 不要用 UserService.class.getAnnotation(Role.class) —— 可能只返回一个或 null
注意 getDeclaredAnnotationsByType() 和 getAnnotationsByType() 的区别
两者都支持重复注解,区别在于继承性:
-
getAnnotationsByType():包含从父类/接口继承来的注解(如果该注解本身标注了@Inherited,且是类级别注解) -
getDeclaredAnnotationsByType():仅限当前元素**直接声明**的注解,不查继承链
大多数场景推荐用 getDeclaredAnnotationsByType(),语义更明确、行为更可控。










