@Inherited仅对类继承有效,不作用于接口实现;其只影响父类上的TYPE级RUNTIME注解,对接口、方法、字段注解无效,且Spring AOP中需绕过代理查找原始类注解。
@Inherited只对类继承有效,不作用于接口实现
这是最核心的限制。@inherited 的 javadoc 明确说明:“this meta-annotation only causes annotations to be inherited from superclasses; annotations on implemented interfaces have no effect”。也就是说,即使你在接口上定义了一个带 @inherited 的注解,并在接口上标注了它,其实现类完全不会自动获得该注解。jvm 反射机制在调用 getannotation() 时,根本不会去检查所实现的接口——哪怕接口上有同名注解、哪怕该注解本身被 @inherited 修饰。
接口方法上的注解无法被 AOP 拦截的常见原因
当把自定义注解(如 @Log)直接标注在接口的方法声明上,再用 @Aspect 切面拦截 @annotation(Log),往往失败。这是因为:
- Spring 默认使用 JDK 动态代理时,生成的代理对象是接口类型,而非实现类类型;此时
joinPoint.getTarget().getClass()返回的是代理类(如$Proxy123),它没有继承任何类,也不持有接口方法上的注解信息 - 即使你给注解加了 @Inherited,它也只适用于类层级的 @Target(ElementType.TYPE),对接口或方法级注解无效
- Spring Framework 5.x 之后才增强对接口方法注解的反射支持,旧版本(如 4.x)基本无法识别接口方法上的运行时注解
注解自身配置不当导致继承失效
即便目标是类继承场景,@Inherited 也需配合其他元注解才能真正生效:
-
@Target 必须包含 ElementType.TYPE:若注解只允许写在方法上(
@Target(METHOD)),即使加了 @Inherited,子类也无法“继承”一个不能写在类上的注解 - @Retention 必须为 RUNTIME:只有保留策略为 RUNTIME,反射时才能读取到注解;CLASS 或 SOURCE 级别下,运行期已不可见
- 不能误用于抽象类/接口的字段或方法:@Inherited 不传递方法注解,父类方法上的 @Inherited 注解,子类重写后不会自动带上——子类方法需单独标注
代理机制掩盖了真实注解位置
在 Spring AOP 场景中,类上标注的注解(如 @DataSource)看似“失效”,往往不是 @Inherited 的问题,而是代理遮蔽了原始类:
- JDK 代理返回的是接口实例,
getClass()拿不到原始 Service 类,自然查不到其上的注解 - CGLIB 代理虽生成子类,但前提是注解本身被 @Inherited 修饰 + @Target(TYPE),否则子类也不会携带
- 正确做法是绕过代理,用
AopProxyUtils.ultimateTargetClass(proxy)或AnnotationUtils.findAnnotation()主动查找原始目标类上的注解

















