【问题标题】:Annotations on an overridden method are ignored in Java 8 PropertyDescriptorJava 8 PropertyDescriptor 中忽略了重写方法上的注释
【发布时间】:2015-03-03 22:49:29
【问题描述】:

我不太清楚如何简洁地描述这个问题,所以请多多包涵。

我正在研究将我们的应用程序从 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。但是,如果我尝试 getAnnotation() 获取在 super getDisplayName() 方法上声明的注释之一,那就没问题了。同样,如果我将 CompositeProperty 注释移动到超类,则测试通过,但实际上这不是一个选项。谁能解释这种行为?

【问题讨论】:

  • 我很想知道propd.getReadMethod().isAnnotationPresent(CompositeProperty.class) 是否返回true。或者,即使Override.class 出现。您是否尝试过其他注释?您是否尝试过使用getDeclaredAnnotation()
  • @XaeroDegreaz:不,错误。覆盖也是错误的。但是我取得了一些进展:我忘了提到 EntityWithCompositeDisplayName 类是在测试类中声明的内部类。如果我将课程提取到顶级课程,则测试通过!所以这与内部类的重写方法的注释有关......
  • 这对我来说听起来像是一个错误,您可能需要考虑使用 oracle 提交它
  • @MarkRotteveel 谢谢。我已将其报告为错误。我想我现在只需要使用解决方法。

标签: java reflection annotations


【解决方案1】:

在 JDK 源代码中稍作挖掘之后,下面是 Introspector 的 JDK8 实现内部发生的情况:

  1. 每个Introspector 首先自省其属性访问器的直接超类。这样做,对于层次结构中的每个类,都会创建一个 Introspector 的实例。超类的自省 getTargetPropertyInfo 在某个时候被子类自省调用,以提取继承的 PropertyDescriptors。
  2. 在提取EntityWithCompositeDisplayName 的属性时,已经提取了Object::getClassEntity::getName 的两个属性。
  3. 提取EntityWithCompositeDisplayName的方法时,Introspector调用getPublicDeclaredMethods(Class),其中任何方法都使用com.sun.beans.MethodFinder::findAccessibleMethod(Method)解析。
  4. 后面的方法检查声明 (!) Class 的方法是否公开,这对于 EntityWithCompositeDisplayName 不正确。因此,它会在类层次结构中返回并尝试分解一个公共超类,该超类声明一个具有相同签名的方法,然后返回该超类的方法。这样做似乎是为了避免从包外部访问包私有类的方法时需要进行的访问检查(有根据的猜测)。

使用MethodFinderMethod 分辨率未在JDK7(或更早版本)中实现。结果,您确实得到了您期望的方法。

对我来说,这似乎是优化的副作用(因此是错误)。

【讨论】:

  • 感谢您的研究/解释!我已经向甲骨文报告了,所以……不知道还能做什么。
  • 可能是要走的路。改变现有类的行为不是很Java,这几乎肯定是一个错误。
【解决方案2】:

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

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();
}

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

【讨论】:

  • 您的测试没有完全重现问题。稍后我会写一个简单的示例测试。
  • 我已经用自己的单元测试重现了这个问题:gist.github.com/gdubya/4d99fef5bf874046f930
  • @GaZ 我无法重现您的要点。我得到一个非空值。
  • 哪个版本的 Java?我在 Linux x64 (Ubuntu) java 版本 "1.8.0_25" Java(TM) SE Runtime Environment (build 1.8.0_25-b17) Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, 混合模式)
  • @GaZ build 1.8.0_20-b26。请回复@myusername,否则我不会收到通知。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-22
  • 2015-08-05
  • 1970-01-01
  • 1970-01-01
  • 2018-07-27
相关资源
最近更新 更多