jbi*_*del 15 java spring javabeans java-7
更新:Oracle已将此确认为错误.
简介:某些在JDK 1.6中工作的自定义BeanInfos和PropertyDescriptors在JDK 1.7中失败,有些只在垃圾收集运行并且清除了某些SoftReferences后才会失败.
编辑:这也将打破
ExtendedBeanInfo在春季3.1,如帖子底部所述.编辑:如果您调用JavaBeans规范的第7.1或8.3节,请准确说明规范的这些部分需要什么.在这些部分中,语言不是必要的或规范性的.这些部分中的语言是示例中的语言,这些示例最好不明确作为规范.此外,
BeanInfoAPI特别允许人们更改默认行为,并在下面的第二个示例中明确区分.
Java Beans规范查找具有void返回类型的默认setter方法,但它允许通过a定制getter和setter方法java.beans.PropertyDescriptor.使用它的最简单方法是指定getter和setter的名称.
new PropertyDescriptor("foo", MyClass.class, "getFoo", "setFoo");
Run Code Online (Sandbox Code Playgroud)
这在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;
}
}
Run Code Online (Sandbox Code Playgroud)
自定义BeanInfos 的示例允许对PropertyDescriptorJava Beans规范中的s进行编程控制,它们都为其setter使用void返回类型,但规范中没有任何内容表明这些示例是规范性的,现在这个低级实用程序的行为已经具有在新的Java类中发生了变化,这恰好打破了我工作的一些代码.
java.beansJDK 1.6和1.7之间的包中有很多变化,但导致此测试失败的变化似乎在这个差异中:
@@ -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) {
Run Code Online (Sandbox Code Playgroud)
PropertyDescriptor现在还检查返回类型以查看它是否为null,而不是简单地接受具有正确名称和参数的方法,因此不再使用流畅的setter.该PropertyDescriptor抛出IntrospectionException在这种情况下:"未找到方法:SETI".
然而,问题比上面的简单测试更加隐蔽.在PropertyDescriptor自定义中指定getter和setter方法的另一种方法BeanInfo是使用实际Method对象:
@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;
}
Run Code Online (Sandbox Code Playgroud)
现在上面的代码将在1.6和1.7中通过单元测试,但代码将在JVM实例的生命周期中的某个时间点开始失败,因为导致第一个示例立即失败的相同更改.在第二个示例中,尝试使用自定义时,唯一指示出现任何问题PropertyDescriptor.setter为null,大多数实用程序代码都认为该属性是只读的.
差异中的代码在里面PropertyDescriptor.getWriteMethod().它在SoftReference保持实际设定器Method为空时执行.此代码由PropertyDescriptor第一个示例中的构造函数调用,该构造函数采用上面的访问器方法名称,因为最初没有Method保存在SoftReference持有实际getter和setter 的s中.
在第二示例中的读取方法和写入方法被存储在SoftReference对象在PropertyDescriptor由构造,并且在第一这些将包含的参考文献readMethod和writeMethodgetter和setter Method给予构造秒.如果在某些时候这些Soft引用被清除,因为允许垃圾收集器(它会这样做),那么getWriteMethod()代码将看到SoftReference返回null,它将尝试发现setter.这次,使用相同的代码路径PropertyDescriptor导致第一个示例在JDK 1.7中失败,它将写入设置Method为null因为返回类型不是void.(返回类型不是 Java 方法签名的一部分.)
使用自定义时,随着时间的推移,这种行为会发生变化,这BeanInfo可能会让人非常困惑.试图复制导致垃圾收集器清除这些特定条件的条件SoftReferences也很繁琐(尽管一些工具模拟可能会有所帮助.)
Spring ExtendedBeanInfo类具有与上面类似的测试.这是一个实际的Spring 3.1.1单元测试ExtendedBeanInfoTest,它将通过单元测试模式,但被测试的代码将在后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));
}
Run Code Online (Sandbox Code Playgroud)
一个建议是我们可以通过防止setter方法只能轻柔地访问来保持当前代码与非void setter一起工作.这似乎可行,但这是对JDK 1.7中改变的行为的破解.
问:是否有一些明确的规范说明非虚空制定者应该是诅咒?我什么都没发现,我现在认为这是JDK 1.7库中的一个错误.我错了,为什么?
由于我发现 Spring 3.1.1ExtendedBeanInfo单元测试期望代码不会被破坏,并且由于垃圾收集后行为发生变化显然是一个错误,因此我将回答这个问题并记下 Java 错误编号。这些错误在 Java 错误的外部数据库中仍然不可见,但我希望它们在某个时候变得可见:
https://bugs.java.com/bugdatabase/view_bug?bug_id=7172854(Oracle将其作为以下错误的重复项关闭,因为尽管表现形式不同,但它们具有相同的原因。)
https://bugs.java.com/bugdatabase/view_bug?bug_id=7172865
(这些错误于 2012 年 5 月 30 日提交。)
截至 2012 年 6 月 20 日,这些错误可通过上面的链接在外部数据库中查看。
| 归档时间: |
|
| 查看次数: |
7086 次 |
| 最近记录: |