Warum hat sich das Verhalten von PropertyDescriptor von Java 1.6 auf 1.7 geändert?

Update: Oracle hat dies als Fehler bestätigt.

Zusammenfassung: Bestimmter BrauchBeanInfos undPropertyDescriptors, die in JDK 1.6 funktionieren, schlagen in JDK 1.7 fehl, und einige schlagen erst fehl, nachdem Garbage Collection bestimmte SoftReferences ausgeführt und gelöscht hat.

Bearbeiten: Dies wird auch die brechenExtendedBeanInfo im Frühjahr 3.1 wie am Ende des Beitrags vermerkt.

Bearbeiten: Wenn Sie Abschnitte 7.1 oder 8.3 der JavaBeans-Spezifikation aufrufen, erläutern Sie genau, wo diese Teile der Spezifikation sindbenötigen etwas. Die Sprache ist in diesen Abschnitten nicht zwingend oder normativ. Die Sprache in diesen Abschnitten ist die von Beispielen, die als Spezifikation bestenfalls mehrdeutig sind. Darüber hinaus ist dieBeanInfo Die API ermöglicht es einem, das Standardverhalten speziell zu ändern, und dies ist im folgenden zweiten Beispiel deutlich zu erkennen.

Die Java Beans-Spezifikation sucht nach Standard-Setter-Methoden mit dem Rückgabetyp void, ermöglicht jedoch die Anpassung der Getter- und Setter-Methoden über ajava.beans.PropertyDescriptor. Die einfachste Möglichkeit, es zu verwenden, bestand darin, die Namen des Get- und Setters anzugeben.

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

Dies hat in JDK 1.5 und JDK 1.6 funktioniert, um den Namen des Setters anzugeben, auch wenn sein Rückgabetyp nicht ungültig ist, wie im folgenden Testfall:

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

Das Beispiel der SitteBeanInfos, die die programmatische Steuerung von ermöglichenPropertyDescriptors in der Java-Beans-Spezifikation verwenden alle void-Rückgabetypen für ihre Setter, aber nichts in der Spezifikation gibt an, dass diese Beispiele normativ sind, und jetzt hat sich das Verhalten dieses Hilfsprogramms auf niedriger Ebene in den neuen Java-Klassen geändert, was zufällig gescheitert ist ein Code, an dem ich arbeite.

Es gibt zahlreiche Änderungen in derjava.beans Paket zwischen JDK 1.6 und 1.7, aber das, bei dem dieser Test fehlschlägt, scheint in diesem Unterschied zu sein:

@@ -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) {

Anstatt einfach die Methode mit dem richtigen Namen und den richtigen Parametern zu akzeptieren, wird diePropertyDescriptor überprüft nun auch den Rückgabetyp, um festzustellen, ob er null ist, sodass der flüssige Setter nicht mehr verwendet wird. DasPropertyDescriptor wirft einIntrospectionException in diesem Fall: "Methode nicht gefunden: setI".

Das Problem ist jedoch viel heimtückischer als der oben beschriebene einfache Test. Eine andere Möglichkeit, die Getter- und Setter-Methoden in der anzugebenPropertyDescriptor für einen BrauchBeanInfo ist die tatsächliche zu verwendenMethod Objekte:

@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;
}

Nun der obige Codewerden Führen Sie einen Komponententest sowohl in 1.6 als auch in 1.7 durch. Der Code schlägt jedoch zu einem bestimmten Zeitpunkt während der Lebensdauer der JVM-Instanz aufgrund derselben Änderung fehl, durch die das erste Beispiel sofort fehlschlägt. Im zweiten Beispiel gibt es nur einen Hinweis darauf, dass etwas schief gelaufen ist, wenn Sie versuchen, die benutzerdefinierte Funktion zu verwendenPropertyDescriptor. Der Setter ist null, und die meisten Utility-Codes bedeuten, dass die Eigenschaft schreibgeschützt ist.

Der Code im Diff ist drinPropertyDescriptor.getWriteMethod(). Es wird ausgeführt, wenn dieSoftReference Halten Sie den aktuellen SetterMethod ist leer. Dieser Code wird von der aufgerufenPropertyDescriptor Konstruktor im ersten Beispiel, der die Zugriffsmethode verwendetNamen oben, weil es anfangs keine gibtMethod gespeichert in derSoftReferences hält den aktuellen Getter und Setter.

Im zweiten Beispiel sind die Lesemethode und die Schreibmethode in gespeichertSoftReference Objekte in derPropertyDescriptor vom Konstruktor, und diese enthalten zunächst Verweise auf diereadMethod undwriteMethod Getter und SetterMethods an den Konstruktor übergeben. Wenn diese weichen Verweise zu einem bestimmten Zeitpunkt gelöscht werden, wie es der Garbage Collector tun darf (und dies auch tun wird), wird dergetWriteMethod() Code wird sehen, dass dieSoftReference gibt null zurück, und es wird versucht, den Setter zu finden.Diesmal, mit dem gleichen Code-Pfad im InnerenPropertyDescriptor Wenn das erste Beispiel in JDK 1.7 fehlschlägt, wird der Schreibvorgang festgelegtMethod zunull weil der Rückgabetyp nicht istvoid. (Der Rückgabetyp istnicht Teil eines JavaMethodensignatur.)

Wenn sich das Verhalten im Laufe der Zeit ändert, wenn Sie eine benutzerdefinierte Einstellung verwendenBeanInfo kann sehr verwirrend sein. Es wurde versucht, die Bedingungen zu duplizieren, unter denen der Garbage Collector diese Bedingungen löschtSoftReferences ist auch langweilig (obwohl vielleicht ein paar instrumentelle Verspottungen helfen können.)

Der FrühlingExtendedBeanInfo Klasse hat ähnliche Tests wie oben. Hier ein aktueller Spring 3.1.1 Unit Test vonExtendedBeanInfoTest Dies wird im Unit-Test-Modus bestanden, aber der zu testende Code schlägt im Post-GC-Modus fehl:

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

Ein Vorschlag ist, dass wir den aktuellen Code für die nicht-leeren Setter beibehalten können, indem wir verhindern, dass die Setter-Methoden nur leicht erreichbar sind. Das scheint zu funktionieren, aber das ist eher ein Hack um das geänderte Verhalten in JDK 1.7.

F: Gibt es eine definitive Spezifikation, die besagt, dass nicht leere Setter Anathema sein sollten? Ich habe nichts gefunden und halte dies derzeit für einen Fehler in den JDK 1.7-Bibliotheken. Irre ich mich und warum?

Antworten auf die Frage(4)

Ihre Antwort auf die Frage