@Inherited仅优化Class.getAnnotation()的继承链查找,不复制注解、不改变字节码;生效需同时满足:注解自身标@Inherited、@Target(ElementType.TYPE)、@Retention(RUNTIME)。

@Inherited不是“继承”,而是反射查找优化
很多人以为加了@Inherited,子类就“自动拥有”父类的注解。其实完全不是这样——@Inherited不会复制、不改变字节码、也不影响运行时对象结构。它只在调用Class.getAnnotation()时,让JVM沿着类继承链向上搜索一次,找到第一个匹配的类级注解就返回。如果用getDeclaredAnnotation(),哪怕父类有注解,子类也一定返回null,因为它只查子类自己声明的部分。
三个硬性条件缺一不可
要让@Inherited生效,必须同时满足:
- 该注解自身被
@Inherited修饰(不能直接在类上写@Inherited) -
@Target(ElementType.TYPE),且仅用于标注类(不能标方法、字段、接口) -
@Retention(RetentionPolicy.RUNTIME),确保运行期可通过反射读取
常见失效场景与真实原因
以下情况@Inherited一律不工作,不是配置错,而是机制本就不支持:
- 接口上定义了带@Inherited的注解,实现类查不到——JVM反射根本不检查接口
- 父类方法或字段上的注解,无论是否加@Inherited,子类重写/访问后都拿不到
- Spring中类被CGLIB代理后,
getClass()返回的是代理子类,但若原始类注解没加@Inherited,代理类也不会携带 - 通过组合获得的注解(如
@Service间接注入),子类无法沿继承链追溯
替代方案:手动遍历比依赖@Inherited更可靠
Java原生不提供跨层级注解查找能力。需要获取“继承链上最近的方法注解”时,必须自己递归:
立即学习“Java免费学习笔记(深入)”;
- 从目标类开始,用
getDeclaredMethod()查当前类声明的方法 - 若未命中,调用
getSuperclass()获取父类,重复查找 - 如需支持接口,还要额外处理
getInterfaces()并递归其父接口(注意避免循环引用)
Spring的AnnotationUtils.findAnnotation()正是这样实现的——它绕过了@Inherited的限制,提供了真正可用的语义化查找。


















