15

更新:Oracleはこれをバグとして確認しました。

概要:JDK1.6で機能する特定のカスタムBeanInfoPropertyDescriptorはJDK1.7で失敗し、一部はガベージコレクションが実行されて特定のSoftReferenceをクリアした後にのみ失敗します。

編集:これはExtendedBeanInfo、投稿の下部に記載されているように、Spring3.1でも機能しなくなります。

編集:JavaBeans仕様のセクション7.1または8.3を呼び出す場合は、仕様のそれらの部分が何かを必要とする場所を正確に説明してください。これらのセクションでは、言語は必須でも規範的でもありません。これらのセクションの言語は例の言語であり、仕様としてはせいぜいあいまいです。さらに、BeanInfoAPIを使用すると、デフォルトの動作を具体的に変更できます。これは、以下の2番目の例では明らかに壊れています。

Java Beans仕様では、return型がvoidのデフォルトのsetterメソッドを探しますが、。を使用してgetterメソッドとsetterメソッドをカスタマイズできますjava.beans.PropertyDescriptor。これを使用する最も簡単な方法は、ゲッターとセッターの名前を指定することです。

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

これは、JDK1.5およびJDK1.6で機能し、以下のテストケースのように、戻りタイプが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;
    }
}

BeanInfoJava Beans仕様のsをプログラムで制御できるカスタムsの例はPropertyDescriptorすべて、セッターに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かどうかを確認するようになったため、流暢なセッターは使用されなくなりました。この場合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;
}

上記のコード1.6と1.7の両方で単体テストに合格しますが、最初の例がすぐに失敗するのとまったく同じ変更により、JVMインスタンスの存続期間中のある時点でコードが失敗し始めます。2番目の例では、カスタムを使用しようとしたときに、問題が発生したことを示す唯一の兆候がありますPropertyDescriptor。セッターはnullであり、ほとんどのユーティリティコードは、プロパティが読み取り専用であることを意味します。

差分のコードはの中にありPropertyDescriptor.getWriteMethod()ます。SoftReference実際のセッターの保持Methodが空のときに実行されます。このコードは、上記PropertyDescriptorのアクセサーメソッドを使用する最初の例のコンストラクターによって呼び出されます。これは、最初は実際のゲッターとセッターを保持するsMethodに保存されていないためです。SoftReference

2番目の例では、読み取りメソッドと書き込みメソッドはコンストラクターによってSoftReferenceオブジェクトに格納されPropertyDescriptor、最初はコンストラクターに指定されたゲッターとセッターreadMethodへの参照が含まれます。ある時点で、ガベージコレクターに許可されている(そして実行できる)ときにこれらのソフト参照がクリアされると、コードは、がnullを返すことを確認し、セッターを検出しようとします。今回は、JDK 1.7で最初の例が失敗する原因となる内部の同じコードパスを使用すると、戻りタイプがではないため、書き込みがに設定されます。(戻り型はJavaメソッドのシグニチャーの一部ではありません。)writeMethodMethodgetWriteMethod()SoftReferencePropertyDescriptorMethodnullvoid

カスタムを使用するときにこのような動作が時間の経過とともに変化BeanInfoすると、非常に混乱する可能性があります。ガベージコレクターがそれらの特定のものをクリアする原因となる条件を複製しようとすることSoftReferencesも退屈です(ただし、いくつかのインストルメンテーションモックが役立つ場合があります)。

SpringExtendedBeanInfoクラスには、上記と同様のテストがあります。これは、ユニットテストモードで合格する実際の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));
}

1つの提案は、setterメソッドがソフトにしか到達できないようにすることで、現在のコードを非voidセッターで機能させ続けることができるということです。それはうまくいくように見えますが、それはJDK1.7で変更された動作を回避するためのハックです。

Q:非ボイドセッターはアナテマであるべきだという明確な仕様はありますか?何も見つかりませんでした。現在、これはJDK1.7ライブラリのバグだと考えています。私は間違っていますか、そしてなぜですか?

4

4 に答える 4

3

仕様は変更されていないように見えますが (void セッターが必要です)、実装が更新されて void セッターのみが許可されています。

仕様:

http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html

より具体的には、セクション 7.1 (アクセサー メソッド) および 8.3 (単純なプロパティの設計パターン) を参照してください。

このスタックオーバーフローの質問で、後の回答のいくつかを参照してください。

Java Bean のセッター許可はこれを返しますか?

于 2012-05-29T21:56:00.140 に答える
1

セクション 8.2 は次のように指定します。

ただし、Java Beans 内では、設計パターンに一致するメソッド名と型名の使用は完全にオプションです。プログラマーが BeanInfo インターフェイスを使用してプロパティ、メソッド、およびイベントを明示的に指定する準備ができている場合は、メソッドと型を好きなように呼び出すことができます。ただし、これらのメソッドと型は、操作に不可欠であるため、必要な型シグネチャと一致する必要があります

(強調追加)

また、7.1 と 8.3 に示されているメソッド シグネチャは、実際には規範的であると信じています。プロパティ名の例として「foo」を使用しているという意味で、これらは単なる例です。

于 2012-05-29T23:52:53.713 に答える
0

コードが壊れていないことを期待するSpring 3.1.1ExtendedBeanInfo単体テストを見つけたので、ガベージ コレクション後に動作が変化するのは明らかにバグであるため、これに回答し、Java バグ番号に注意します。バグは、Java バグの外部データベースにはまだ表示されていませんが、いつか表示されるようになることを願っています。

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7172854 (オラクルは、以下のバグの重複としてこれを閉じました。これは、症状が異なるにもかかわらず原因が同じであるためです。)

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7172865

(バグは 2012 年 5 月 30 日に提出されました。)

2012 年 6 月 20 日の時点で、バグは上記のリンクから外部データベースに表示されます。

于 2012-06-06T19:01:20.880 に答える