必须用getDeclaredConstructors()获取全部构造函数(含private/protected),再对每个Constructor调用getDeclaredAnnotation(YourAnnotation.class)或getAnnotationsByType(),且注解须声明@Retention(RUNTIME)。

如何用 getDeclaredConstructors() 获取带注解的构造函数
Java 反射中,getConstructors() 只返回 public 构造函数,哪怕你加了自定义注解,只要构造函数是 private 或 protected,就直接被过滤掉了。必须用 getDeclaredConstructors() 才能拿到全部声明的构造函数——包括非 public 的。
实操建议:
- 调用
clazz.getDeclaredConstructors(),不是getConstructors() - 对每个返回的
Constructor对象,再调用getDeclaredAnnotation(YourAnnotation.class)或getDeclaredAnnotations() - 别忘了在遍历前调用
setAccessible(true)(尤其当构造函数不可见时,虽不直接影响注解读取,但后续实例化会需要)
为什么 getAnnotation() 返回 null 而 getDeclaredAnnotation() 才有效
这是反射里最容易踩的坑:注解默认的 @Retention 策略决定它是否保留在运行时。如果自定义注解没显式声明 @Retention(RetentionPolicy.RUNTIME),那它根本不会进入字节码的 RuntimeVisibleAnnotations 属性,反射自然查不到。
更关键的是语义差异:
-
getAnnotation(Class只查找该构造函数**自身直接声明**的注解,不查继承来的(构造函数本身不能继承,所以这点影响不大)) -
getDeclaredAnnotation(Class是标准推荐方式,明确表示“我要这个元素上直接写的注解”,行为更可预期) - 即使注解有
@Inherited,它也只对Class上的注解生效,对构造函数、方法、字段完全无效
处理多个构造函数 + 多个同类型注解的场景
一个类可能有多个构造函数,每个都可能标了同一个自定义注解;甚至一个构造函数上可能重复标注了多次(前提是注解声明了 @Repeatable)。这时候不能只用 getDeclaredAnnotation() —— 它只返回第一个或 null。
正确做法:
- 用
constructor.getAnnotationsByType(YourAnnotation.class)替代getDeclaredAnnotation(),它自动处理@Repeatable容器逻辑,并返回所有匹配实例 - 如果注解未声明
@Repeatable,但你仍想安全获取(避免空指针),用constructor.getDeclaredAnnotation(YourAnnotation.class)并判空 - 注意:不要用
getAnnotations()全量遍历再 instanceof 过滤——性能差,且无法区分同类型不同实例的语义
常见错误现象与调试线索
典型表现:代码跑通但始终拿不到注解,或者只拿到部分。
- 编译时报错 “cannot find symbol” 注解类 → 检查注解类是否在 classpath,且 import 正确(别漏掉包名)
- 运行时返回 null → 优先检查注解定义里有没有
@Retention(RetentionPolicy.RUNTIME) - 注解在 IDE 里显示正常,但反射读不到 → 确认你操作的是目标类的
Class对象,不是父类或代理类(比如 Spring CGLIB 代理类的getClass()返回的是子类,不是原始类) - 用 Lombok 的
@AllArgsConstructor或@RequiredArgsConstructor→ 它们生成的构造函数在字节码里存在,但源码无显式声明,因此你无法在源码上加注解;必须用@Tolerate或手动写构造函数再标注
注解反射本身不慢,但频繁调用 getDeclaredConstructors() + 遍历建议缓存结果;真正容易被忽略的是注解元信息配置和构造函数可见性这两层,缺一不可。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











