我不太清楚如何简洁地描述这个问题,所以请耐心等待.
我正在研究将我们的应用程序从Java 7升级到Java 8.该应用程序依赖于使用反射来动态创建各种业务实体的实例(所谓的“Adaptive Object Model”).当我尝试使用最新的Java 8(Oracle 1.8.0_25)构建应用程序时,一些测试开始失败.具体来说,有一个测试,它检查“测试实体”中的getter方法是否使用特定注释进行注释,并且由于未找到注释,测试失败.超级“实体”类具有一些适用于所有实体实例(名称,描述,显示名称等)的通用字段,而在测试类中,“测试实体”扩展“实体”并覆盖其中一个getter方法(getDisplayName) )并添加一个额外的注释:
static class EntityWithCompositeDisplayName extends Entity {
@CompositeProperty("${name} - ${description}")
@Override
public String getDisplayName() {
return super.getDisplayName();
}
}
@CompositeProperty是我们自己的注释.它具有METHOD目标和RUNTIME保留.
该测试调用以下代码以查看是否存在注释:
CompositeProperty compositeProperty = propd.getReadMethod().getAnnotation(CompositeProperty.class);
“compositeProperty”在Java 8中返回null,但在Java 7中不返回null.但是如果我尝试在超级getDisplayName()方法上声明其中一个注释的getAnnotation(),那么它就没问题了.类似地,如果我将CompositeProperty注释移动到超类然后测试通过,但实际上这不是一个选项.谁能解释这种行为?
最佳答案 在对JDK源代码进行一些挖掘之后,以下是Introspector的JDK8实现中发生的情况:
>每个Introspector首先对其属性访问器的直接超类进行内省.这样做,对于层次结构中的每个类,都会创建一个Introspector实例.超类的introspector的getTargetPropertyInfo由子类instrospector调用一些点,以提取继承的PropertyDescriptors.
>在提取EntityWithCompositeDisplayName的属性时,已经提取了Object :: getClass和Entity :: getName的两个属性.
>在提取EntityWithCompositeDisplayName的方法时,Introspector调用getPublicDeclaredMethods(Class),其中使用com.sun.beans.MethodFinder :: findAccessibleMethod(Method)解析任何方法.
>后一种方法检查方法的声明(!)类是否公开,而EntityWithCompositeDisplayName则不然.因此,它遍历类层次结构并尝试遍历声明具有相同签名的方法的公共超类,然后返回超类的方法.看起来这样做是为了避免在从包外部访问package-private类的方法时需要进行访问检查(有根据的猜测).
使用MethodFinder的Method解决方案未在JDK7(或更早版本)中实现.因此,您确实获得了预期的方法.
看起来像是对我的优化(因此也是一个错误)的副作用.