복붙노트

[SPRING] PropertyDescriptor의 동작이 Java 1.6에서 1.7로 변경된 이유는 무엇입니까?

SPRING

PropertyDescriptor의 동작이 Java 1.6에서 1.7로 변경된 이유는 무엇입니까?

업데이트 : 오라클은 이것을 버그로 확인했습니다.

요약 : JDK 1.6에서 작동하는 특정 사용자 정의 BeanInfos 및 PropertyDescriptors는 JDK 1.7에서 실패하고 가비지 수집이 실행되고 특정 SoftReferences를 지운 후에 만 ​​실패하는 일부가 있습니다.

Java Beans 사양은 void 반환 형식의 기본 setter 메서드를 검색하지만 java.beans.PropertyDescriptor를 통해 getter 및 setter 메서드를 사용자 지정할 수 있습니다. 가장 간단한 방법은 getter와 setter의 이름을 지정하는 것입니다.

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

JDK 1.5와 JDK 1.6에서는 아래의 테스트 케이스에서와 같이 반환 유형이 void가 아닌 경우에도 setter 이름을 지정하는 데 사용되었습니다.

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

Java Beans 스펙의 PropertyDescriptors를 프로그래밍 방식으로 제어 할 수있는 사용자 정의 BeanInfos의 예제는 모두 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인지 여부를 확인하므로 유창한 설정자가 더 이상 사용되지 않습니다. 이 경우 PropertyDescriptor는 IntrospectionException을 발생시킵니다. "Method not found : setI".

그러나 문제는 위의 간단한 테스트보다 훨씬 더 교활합니다. 커스텀 BeanInfo에 대한 PropertyDescriptor에서 getter와 setter 메소드를 지정하는 또 다른 방법은 실제 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 인스턴스가 사용되는 동안 특정 시점에서 코드가 실패하기 시작합니다. 두 번째 예제에서는 사용자 정의 PropertyDescriptor를 사용하려고 할 때 잘못된 것이 있다는 유일한 표시가 나타납니다. setter는 null이며, 대부분의 유틸리티 코드는 속성이 읽기 전용임을 의미합니다.

diff의 코드는 PropertyDescriptor.getWriteMethod () 안에 있습니다. 실제의 Setter Method를 보관 유지하는 SoftReference가 하늘 일 때 실행됩니다. 이 코드는 처음에는 실제 getter 및 setter를 보유한 SoftReferences에 저장된 Method가 없으므로 위의 접근 자 메서드 이름을 사용하는 첫 번째 예제의 PropertyDescriptor 생성자에 의해 호출됩니다.

두 번째 예제에서 read 메소드와 write 메소드는 PropertyDescriptor의 SoftReference 객체에 생성자에 의해 저장되며, 처음에는 생성자에게 주어진 readMethod 및 writeMethod getter 및 setter 메소드에 대한 참조를 포함합니다. 가비지 콜렉터가 허용되는대로 소프트 참조가 지워지면 getWriteMethod () 코드는 SoftReference가 null을 돌려주는 것을보고 setter를 발견하려고 시도합니다. 이번에는 JDK 1.7에서 첫 번째 예제가 실패하는 PropertyDescriptor 내부의 동일한 코드 경로를 사용하여 반환 유형이 void가 아니므로 write 메소드를 null로 설정합니다. 리턴 유형은 Java 메소드 서명의 일부가 아닙니다.

커스텀 BeanInfo를 사용할 때 시간이 지남에 따라 동작이 변경되는 것은 매우 혼란 스러울 수 있습니다. 가비지 컬렉터가 특정 SoftReferences를 지우는 조건을 복제하려고하면 지루합니다 (일부 조롱을해도 도움이 될 수 있습니다).

Spring ExtendedBeanInfo 클래스에는 위와 비슷한 테스트가 있습니다. 다음은 유닛 테스트 모드에서 전달할 ExtendedBeanInfoTest의 실제 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와 함께 현재 코드를 유지할 수 있다는 것입니다. JDK 1.7에서 변경된 동작을 둘러싼 해킹입니다.

Q : 비 void setter가 저주가되어야한다는 몇 가지 명확한 명세가 있습니까? 나는 아무것도 찾지 못했고, 현재이 문제는 JDK 1.7 라이브러리의 버그라고 생각합니다. 나는 틀린가, 그리고 왜?

해결법

  1. ==============================

    1.스펙이 변경되지 않은 것처럼 보입니다 (void setter가 필요합니다).하지만 void setter 만 허용하도록 구현되었습니다.

    스펙이 변경되지 않은 것처럼 보입니다 (void setter가 필요합니다).하지만 void setter 만 허용하도록 구현되었습니다.

    사양:

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

    보다 구체적으로는 7.1 절 (접근 자 메소드)과 8.3 절 (단순 속성을위한 디자인 패턴)

    이 stackoverflow 질문의 이후 답변 중 일부를 참조하십시오.

    Java bean의 setter 허가가 이것을 반환합니까?

  2. ==============================

    2.섹션 8.2에서는 다음을 지정합니다.

    섹션 8.2에서는 다음을 지정합니다.

    (강조가 추가됨)

    또한, 7.1과 8.3에 나와있는 메소드 서명은 실제로 규범 적입니다. 그것들은 예제 속성 이름으로 "foo"를 사용한다는 의미에서만 예제입니다.

  3. ==============================

    3.나는 또한 무효화 된 setter를 허용하지 않는 것이 오류라고 말하기를 원할 것이다. 단순히 유창한 프로그래밍을 불가능하게 만듭니다. 그것이 왜 바뀌어야하는지.

    나는 또한 무효화 된 setter를 허용하지 않는 것이 오류라고 말하기를 원할 것이다. 단순히 유창한 프로그래밍을 불가능하게 만듭니다. 그것이 왜 바뀌어야하는지.

  4. ==============================

    4.가비지 콜렉션 이후의 동작 변경이 분명히 버그이기 때문에 Spring 3.1.1 ExtendedBeanInfo 유닛 테스트가 코드를 깨뜨리지 않을 것으로 예상했기 때문에이 질문에 대답하고 Java 버그 번호를 기록합니다. Java 버그의 외부 데이터베이스에서는 여전히 버그가 표시되지 않지만 어느 시점에서는 볼 수있게되기를 바랍니다.

    가비지 콜렉션 이후의 동작 변경이 분명히 버그이기 때문에 Spring 3.1.1 ExtendedBeanInfo 유닛 테스트가 코드를 깨뜨리지 않을 것으로 예상했기 때문에이 질문에 대답하고 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 일부터 위의 링크를 통해 버그가 외부 데이터베이스에 표시됩니다.

  5. from https://stackoverflow.com/questions/10806895/why-did-propertydescriptor-behavior-change-from-java-1-6-to-1-7 by cc-by-sa and MIT license