Java 8 PropertyDescriptor中忽略了重写方法的注释

时间:2014-10-22 18:56:06

标签: java reflection annotations

我不太清楚如何简洁地描述这个问题,所以请耐心等待。

我正在研究将我们的应用程序从Java 7升级到Java 8.该应用程序依赖于使用反射来动态创建各种业务实体的实例(所谓的"Adaptive Object Model")。当我尝试使用最新的Java 8(Oracle 1.8.0_25)构建应用程序时,一些测试开始失败。具体来说,有一个测试可以检查“测试实体”中是否有一个getter方法。使用特定注释进行注释,并且测试失败,因为找不到注释。超级"实体" class有一些通用字段,适用于所有实体实例(名称,描述,显示名称等),在测试类中有"测试实体"扩展"实体"并覆盖其中一个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注释移动到超类然后测试通过,但实际上这不是一个选项。谁能解释这种行为?

2 个答案:

答案 0 :(得分:2)

在对JDK源代码进行一些挖掘之后,以下是Introspector的JDK8实现中发生的事情:

  1. 每个Introspector首先对其属性访问者的直接超类进行内省。这样做,对于层次结构中的每个类,都会创建Introspector的实例。超类的introspector的getTargetPropertyInfo由子类instrospector调用,以提取继承的PropertyDescriptor
  2. 在提取EntityWithCompositeDisplayName的属性时,已提取Object::getClassEntity::getName的两个属性。
  3. 在提取EntityWithCompositeDisplayName的方法时,Introspector调用getPublicDeclaredMethods(Class),使用com.sun.beans.MethodFinder::findAccessibleMethod(Method)解析任何方法。
  4. 后一种方法检查方法的声明(!)Class是否公开,而EntityWithCompositeDisplayName则不然。因此,它遍历类层次结构并尝试遍历声明具有相同签名的方法的公共超类,然后返回超类的方法。似乎这样做是为了避免在从包外部访问包私有类的方法时需要进行访问检查(有根据的猜测)。
  5. 使用Method的{​​{1}}分辨率未在JDK7(或更早版本)中实现。因此,您确实获得了预期的方法。

    看起来像是对我的优化(因此也是一个错误)的副作用。

答案 1 :(得分:0)

我很好奇你的问题,我试图复制环境:

public class MyAnnotatedClass extends MyAnnotatedSuperClass{

    @MyCustomAnnotation(name="annotationName", value="annotationValue")
    @Override
    public String getMyString() {
        return myString;
    }

    public static void main(String args[])
    {
        Class<MyAnnotatedClass> myClass =  MyAnnotatedClass.class;

        MyCustomAnnotation myCustomAnnotation = myClass.getMethods()[0].getAnnotation(MyCustomAnnotation.class);

        if(myCustomAnnotation!=null)
            System.out.println("ok: "+myCustomAnnotation.name());
        else
            System.out.println("not ok");
    }
}

abstract class MyAnnotatedSuperClass {
    public String myString;
    public abstract String getMyString();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)

public @interface MyCustomAnnotation {
     public String name();
     public String value();
}

从我的快速测试中,我会得出结论,覆盖方法中的注释工作正常,因此问题可能出在程序的其他方面。