Java注解默认不继承,@Inherited仅适用于类注解且对方法无效;子类重写方法后需沿继承链向上查找父类中原始声明的方法才能获取其注解,可借助Spring的AnnotationUtils或自定义缓存工具实现。

这个问题其实不在于“破解死结”,而在于理解一个关键前提:Java 中的注解默认不会被继承,更不会因子类覆盖方法就自动迁移或重新绑定。所谓“读取死结”,往往是误以为父类方法上的注解会随重写方法一同出现在子类中,但事实并非如此。
注解本身不具备继承性
除非显式声明 @Inherited(且仅对 CLASS 或 RUNTIME 生命周期有效),否则注解不会被子类继承。更重要的是:@Inherited 只作用于类级别注解,对方法注解完全无效。也就是说,哪怕你在父类方法上加了 @Deprecated 或自定义注解,子类重写该方法后,getDeclaredMethod("xxx") 返回的方法对象上不会自动携带父类方法的注解。
- 父类方法有
@Loggable,子类重写后调用method.getAnnotation(Loggable.class)→ 返回null - 子类方法若没显式标注,反射查不到任何该注解
- 这不是反射“读取失败”,而是设计使然——注解依附于具体方法声明,不是继承关系的一部分
如何正确获取父类方法上的注解
既然子类方法自身没有继承注解,那就得主动向上查找。核心思路是:先定位到子类重写的方法,再顺着继承链回溯,找到它最初声明的位置(即父类中那个被重写的方法)。
- 用
clazz.getDeclaredMethod(name, paramTypes...)获取子类声明的方法(可能为空) - 若未找到,调用
clazz.getSuperclass().getDeclaredMethod(...)向上尝试 - 逐层遍历父类,直到
Object.class或找到带目标注解的方法为止 - 注意:需用
getDeclaredMethod而非getMethod,后者只返回 public 方法,可能跳过 protected 或 package-private 的原始声明
避免手动遍历的实用技巧
不必每次都手写递归查找。可借助工具类简化逻辑:
- Spring 的
AnnotationUtils.findAnnotation(Method, Class)会自动沿继承链搜索方法注解 - Apache Commons Lang 的
MethodUtils.getAccessibleMethod(Class, String, Class...)配合反射读取更稳妥 - 自定义工具方法时,记得缓存结果——继承链查找是高频低效操作,重复执行影响性能
特别注意泛型擦除带来的干扰
如果父类是泛型类(如 Service<t></t>),其方法签名在字节码中已被擦除为 Object。此时用 String.class 去反射查找 setT(T t) 会失败。正确做法是:
- 用
getDeclaredMethods()获取全部方法 - 遍历筛选方法名匹配、参数数量一致的候选方法
- 再通过
method.getGenericParameterTypes()检查泛型类型信息(如ParameterizedType) - 结合
getDeclaringClass()确认是否来自父类声明

















