@inherited仅在子类调用getannotation()时,使满足@inherited、@target(type)、@retention(runtime)三条件的类级注解可见;不适用于接口、方法、字段,且子类显式声明同名注解会覆盖父类值,实际应手动遍历继承链获取注解。

Java 中的 @Inherited 并不是“注解继承”的通用解决方案,而是一个作用范围极窄的运行时反射辅助机制——它只让子类在调用 getAnnotation() 时,能“看到”父类上标注的、满足特定条件的类级注解。一旦脱离这个狭窄路径,就完全失效。
只对类生效,且三条件缺一不可
一个注解要被子类“继承”,必须同时满足:
- 自身声明了
@Inherited -
@Target(ElementType.TYPE)(不能标在方法、字段等位置) -
@Retention(RetentionPolicy.RUNTIME)(编译后保留在字节码中,运行时可读)
三者少一个,子类调用 clazz.getAnnotation(MyAnno.class) 就返回 null。例如,即使你给方法注解加了 @Inherited,子类重写该方法后依然拿不到父类方法上的注解。
不跨接口,不跨方法,不跨字段
@Inherited 的作用域是硬编码在 JVM 规范里的,仅限「类直接继承」这一种关系:
- 接口上有
@Inherited注解 → 实现类 完全不可见 - 父类方法上标注了注解 → 子类重写后 不会自动携带
- 父类字段上有注解 → 子类通过
getField()或getDeclaredField()查不到任何继承痕迹
这不是 bug,而是设计如此。JVM 不允许方法或字段注解沿继承链传播,这是语言层面的限制。
子类显式声明会彻底屏蔽父类注解
只要子类自己写了同名注解,哪怕只是空写 @MyAnno,父类的注解就立即失效:
-
@MyAnno("A") class Parent→class Child extends Parent:子类能读到"A" -
@MyAnno("A") class Parent→@MyAnno class Child extends Parent:子类读到的是默认值(或空),父类值被覆盖
这种“就近优先”规则意味着 @Inherited 实际上只在子类“完全未干预”的前提下才起作用,业务中很难稳定依赖。
替代方案:手动扫描才是可靠做法
真正需要跨层级聚合注解语义(比如权限检查、配置合并)时,应绕过 @Inherited,改用主动遍历:
- 对类:循环调用
clazz = clazz.getSuperclass(),每层都试getAnnotation() - 对接口:递归展开
clazz.getInterfaces(),包括间接实现的接口 - 对方法:按签名向上查找父类同名方法,逐个检查
getMethod().getAnnotation() - 封装成工具方法,统一处理“最近有效注解”逻辑,比依赖
@Inherited更可控
框架如 Spring、MyBatis 内部正是这样做的——它们从不把注解传递逻辑交给 JVM 的 @Inherited。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











