@Inherited仅对类继承(extends)有效,对接口实现(implements)、子接口继承、方法级注解均无效;其作用范围严格限于TYPE且RUNTIME保留的类层级注解。

@Inherited 不是“继承注解”的通用开关,它只对 类直接继承(extends) 有效,对接口实现(implements)完全不生效——这不是 bug,而是 Java 规范的明确设计。
接口上的 @Inherited 注解,实现类根本看不到
JVM 反射机制在调用 clazz.getAnnotation() 时,只检查类自身的声明和它的父类链(getSuperclass()),完全忽略所实现的接口。哪怕接口上标注了带 @Inherited 的注解,实现类调用 getAnnotation() 也一定返回 null。
- 接口是契约,不是父类;@Inherited 的语义仅绑定在
extends关系上 - 即使自定义注解同时满足 @Inherited + @Target(TYPE) + @Retention(RUNTIME),标在接口上也无效
- 子接口继承父接口(
interface B extends A)同样不会继承任何注解
方法级注解更别指望 @Inherited 传递
把注解(比如 @Log、@Retryable)写在接口的方法声明上,实现类重写该方法后,method.getAnnotation() 拿不到——因为 @Inherited 对方法、字段、参数等位置一律不作用。
- JVM 规范规定:方法对象只保留自身声明处的注解,不合并父类或接口的方法注解
- Spring AOP 默认基于目标方法反射查注解;接口方法有注解,但实现类方法没标,切点就匹配失败
- JDK 动态代理生成的
$ProxyXX类既不继承实现类,也不携带接口方法的注解信息
你以为失效,其实是用错了地方
很多“注解没生效”的问题,根源不在 @Inherited,而在注解自身配置或使用方式不对:
立即学习“Java免费学习笔记(深入)”;
- @Target 必须包含
ElementType.TYPE:若只允许标在方法上(@Target(METHOD)),加 @Inherited 也没意义 - @Retention 必须是
RUNTIME:CLASS 或 SOURCE 级别下,运行期根本不可见 - Spring 场景中,
joinPoint.getTarget().getClass()返回的是代理类,不是原始实现类——得用AopProxyUtils.ultimateTargetClass(proxy)才能查到真实类上的注解
真正可行的替代方案
想让实现类感知接口的元信息,不能靠 @Inherited,得主动处理:
- 在实现类的方法上手动重复标注相同注解(适合少量实现)
- 通过
clazz.getInterfaces()获取所有接口,再遍历每个接口的方法,调用method.getAnnotation() - 改用抽象基类代替接口,把注解放在抽象类上,并确保该注解满足 @Inherited + @Target(TYPE) + @Retention(RUNTIME)
- 编写通用工具方法,递归扫描类的整个继承链(superclass)和全部接口(含间接接口),统一收集注解


















