Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/395.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么PropertyDescriptor行为从Java 1.6更改为1.7?_Java_Spring_Javabeans_Java 7 - Fatal编程技术网

为什么PropertyDescriptor行为从Java 1.6更改为1.7?

为什么PropertyDescriptor行为从Java 1.6更改为1.7?,java,spring,javabeans,java-7,Java,Spring,Javabeans,Java 7,更新:Oracle已确认这是一个bug 摘要:在JDK1.6中工作的某些自定义BeanInfos和PropertyDescriptors在JDK1.7中失败,一些只有在垃圾收集运行并清除某些软引用后才会失败 编辑:这也将打破Spring3.1中的扩展Beaninfo,如文章底部所述 编辑:如果调用JavaBeans规范的7.1或8.3节,请解释 规范中那些部分需要什么的地方。这个 在这些章节中,语言不是强制性的或规范性的。这个 这些章节中的语言都是示例,这些示例充其量是 模棱两可的规范。此外,B

更新:Oracle已确认这是一个bug

摘要:在JDK1.6中工作的某些自定义
BeanInfo
s和
PropertyDescriptor
s在JDK1.7中失败,一些只有在垃圾收集运行并清除某些软引用后才会失败

编辑:这也将打破Spring3.1中的
扩展Beaninfo
,如文章底部所述

编辑:如果调用JavaBeans规范的7.1或8.3节,请解释 规范中那些部分需要什么的地方。这个 在这些章节中,语言不是强制性的或规范性的。这个 这些章节中的语言都是示例,这些示例充其量是 模棱两可的规范。此外,
BeanInfo
API 特别允许更改默认行为,并且 在下面的第二个例子中,这显然是不正确的

JavaBeans规范寻找具有void返回类型的默认setter方法,但它允许通过
Java.Beans.PropertyDescriptor
自定义getter和setter方法。使用它的最简单方法是指定getter和setter的名称

new PropertyDescriptor("foo", MyClass.class, "getFoo", "setFoo");
这在JDK 1.5和JDK 1.6中起到了作用,可以指定setter名称,即使其返回类型不是void,如下面的测试用例所示:

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import org.testng.annotations.*;

/**
 * Shows what has worked up until JDK 1.7.
 */
public class PropertyDescriptorTest
{
    private int i;
    public int getI() { return i; }
    // A setter that my people call "fluent".
    public PropertyDescriptorTest setI(final int i) { this.i = i; return this; }

    @Test
    public void fluentBeans() throws IntrospectionException
    {
        // This throws an exception only in JDK 1.7.
        final PropertyDescriptor pd = new PropertyDescriptor("i",
                           PropertyDescriptorTest.class, "getI", "setI");

        assert pd.getReadMethod() != null;
        assert pd.getWriteMethod() != null;
    }
}
自定义
BeanInfo
s的示例允许对Java Beans规范中的
PropertyDescriptor
s进行编程控制,它们的setter都使用void返回类型,但规范中没有任何内容表明这些示例是规范性的,现在这个低级实用程序的行为在新的Java类中发生了变化,这恰好破坏了我正在处理的一些代码

JDK 1.6和1.7之间的
java.beans
包中有许多变化,但导致此测试失败的原因似乎在于以下差异:

@@ -240,11 +289,16 @@
        }

        if (writeMethodName == null) {
-       writeMethodName = "set" + getBaseName();
+                writeMethodName = Introspector.SET_PREFIX + getBaseName();
        }

-       writeMethod = Introspector.findMethod(cls, writeMethodName, 1, 
-                 (type == null) ? null : new Class[] { type });
+            Class[] args = (type == null) ? null : new Class[] { type };
+            writeMethod = Introspector.findMethod(cls, writeMethodName, 1, args);
+            if (writeMethod != null) {
+                if (!writeMethod.getReturnType().equals(void.class)) {
+                    writeMethod = null;
+                }
+            }
        try {
        setWriteMethod(writeMethod);
        } catch (IntrospectionException ex) {
PropertyDescriptor
不再简单地接受具有正确名称和参数的方法,而是检查返回类型是否为null,因此不再使用fluent setter。在本例中,
PropertyDescriptor
抛出一个
IntrospectionException
,“未找到方法:setI”

然而,这个问题比上面的简单测试更加隐蔽。在自定义
BeanInfo
PropertyDescriptor
中指定getter和setter方法的另一种方法是使用实际的
方法
对象:

@Test
public void fluentBeansByMethod()
    throws IntrospectionException, NoSuchMethodException
{
    final Method readMethod = PropertyDescriptorTest.class.getMethod("getI");
    final Method writeMethod = PropertyDescriptorTest.class.getMethod("setI",
                                                                 Integer.TYPE);

    final PropertyDescriptor pd = new PropertyDescriptor("i", readMethod,
                                                         writeMethod);

    assert pd.getReadMethod() != null;
    assert pd.getWriteMethod() != null;
}
现在,上面的代码将通过1.6和1.7中的单元测试,但在JVM实例生命周期的某个时间点,由于导致第一个示例立即失败的相同更改,代码将开始失败。在第二个示例中,尝试使用自定义
PropertyDescriptor
时,唯一的错误指示出现。setter为null,大多数实用程序代码将其视为只读属性

diff中的代码位于
PropertyDescriptor.getWriteMethod()中。当持有实际setter
方法的
SoftReference
为空时执行。在第一个示例中,
PropertyDescriptor
构造函数调用此代码,该示例采用上述访问器方法名称,因为最初在
SoftReference
中没有保存实际getter和setter的
方法

new PropertyDescriptor("foo", MyClass.class, "getFoo", "setFoo");

在第二个示例中,读方法和写方法由构造函数存储在
PropertyDescriptor
中的
SoftReference
对象中,首先它们将包含对
readMethod
writeMethod
方法的引用。如果在某个时刻,这些软引用被清除,就像垃圾收集器被允许做的那样(它也会这么做),那么
getWriteMethod()
代码将看到
SoftReference
返回null,它将尝试发现setter。这一次,使用导致JDK 1.7中第一个示例失败的
PropertyDescriptor
中的相同代码路径,它将把write
方法设置为
null
,因为返回类型不是
void
。(返回类型不是Java语言的一部分。)

在使用自定义
BeanInfo
时,随着时间的推移,行为会发生这样的变化,这可能会让人非常困惑。尝试复制导致垃圾收集器清除那些特定的
SoftReferences
的条件也是乏味的(尽管一些检测模拟可能会有所帮助)

Spring
ExtendedBeanInfo
类的测试与上面的测试类似。下面是来自
extendedbeanninfotest
的实际Spring 3.1.1单元测试,它将在单元测试模式下通过,但被测试的代码将在后GC隐蔽模式下失败:

@Test
public void nonStandardWriteMethodOnly() throws IntrospectionException {
    @SuppressWarnings("unused") class C {
        public C setFoo(String foo) { return this; }
    }

    BeanInfo bi = Introspector.getBeanInfo(C.class);
    ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);

    assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
    assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));

    assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
    assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
}
一个建议是,我们可以通过防止setter方法只能软访问来保持当前代码与非void setter一起工作。这似乎是可行的,但这是对JDK1.7中改变的行为的一种攻击

问:是否有明确的规范规定非无效设置者应该被诅咒?我找不到任何东西,目前我认为这是JDK 1.7库中的一个bug。
我错了吗?为什么?

看起来规范没有改变(它需要void setter),但是实现已经更新为只允许void setter

规格:

具体请参见第7.1节(访问器方法)和第8.3节(简单属性的设计模式)

请参阅此问题后面的部分答案:

第8.2节规定:

然而,在JavaBean中,使用与设计模式匹配的方法和类型名是完全可选的。如果程序员准备显式指定其属性、方法和