Java反射getAnnotation无法获取父类继承注解?技术疑问
你的理解确实存在一点偏差,咱们一步步理清楚:
核心原因:注解的@Inherited元注解是关键
Java原生的getAnnotation()方法只会在当前类直接声明注解时返回它,或者当目标注解被标记了@Inherited元注解时,才会向上查找父类的同名注解。
你定义的ReadOnlyViewable注解没有添加@Inherited,所以即使父类标注了这个注解,子类调用getClass().getAnnotation(ReadOnlyViewable.class)时,只会检查子类自己有没有直接声明该注解,找不到就返回null——这就是你遇到的情况。
而getDeclaredAnnotation()的行为和你理解的一致:它只检查当前类直接声明的注解,完全不会管父类。
解决方法
针对你的场景,有几种可行的方案:
1. 给ReadOnlyViewable添加@Inherited元注解
这是最简单的方式,修改你的注解定义:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Inherited // 新增这个元注解 public @interface ReadOnlyViewable { String description(); String category(); }
添加后,子类(比如CounterpartyCacheJmx)调用getAnnotation()时,就会自动向上遍历父类,找到父类上的ReadOnlyViewable注解并返回。
⚠️ 注意:@Inherited只对类级别的注解生效,接口上的注解即使加了这个元注解,实现类也不会继承;另外如果子类自己声明了同类型的注解,会覆盖父类的注解。
2. 手动遍历类继承链查找
如果不能修改ReadOnlyViewable注解(比如它是第三方库的注解),可以自己写工具方法遍历类的父类链:
public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) { while (clazz != null && clazz != Object.class) { A annotation = clazz.getDeclaredAnnotation(annotationType); if (annotation != null) { return annotation; } clazz = clazz.getSuperclass(); } return null; }
调用这个方法时,传入CounterpartyCacheJmx.class和ReadOnlyViewable.class,就能找到父类上的注解。
3. 使用第三方库的工具类
很多成熟的框架都提供了注解查找的工具类,比如:
- Spring的
org.springframework.core.annotation.AnnotationUtils.findAnnotation():可以自动遍历类的继承链、接口甚至注解的元注解来查找目标注解,这也是Spring的ApplicationContext.getBeansWithAnnotation()能找到这些bean的原因——Spring内部就是用这类工具做了深度注解解析。 - Guava的
com.google.common.annotations.Annotations.findAnnotation():同样支持遍历父类查找注解。
关于Spring的getBeansWithAnnotation
Spring的这个方法之所以能找到你的CounterpartyCacheJmx实例,是因为它的注解解析逻辑远不止Java原生的getAnnotation():它会递归检查当前类的所有父类、实现的接口,甚至会处理注解的继承关系(比如通过@AliasFor等Spring特有的元注解),本质上是做了全链路的注解扫描,而不是依赖Java原生的注解继承机制。
内容的提问来源于stack exchange,提问作者Tim B

