Java JDK 8 IndexedPropertyDescriptor has changed s

2020-07-09 10:54发布

问题:

I have a simple issue. I have a program working in Java JDK7 but it doesn't work in JDK8 because of some introspection changes.

Here is a test program to reproduce the issue:

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[] args) throws IntrospectionException {
        BeanInfo info = Introspector.getBeanInfo(MyListClass.class);
        PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
        for (int i = 0; i < descriptors.length; i++) {
            System.out.println(descriptors[i].getClass().getName() + ":" + descriptors[i].getName());
        }

        System.out.println("\n");

        BeanInfo info2 = Introspector.getBeanInfo(MyIndexedListClass.class);
        PropertyDescriptor[] descriptors2 = info2.getPropertyDescriptors();
        for (int i = 0; i < descriptors2.length; i++) {
            System.out.println(descriptors2[i].getClass().getName() + ":" + descriptors2[i].getName());
        }

        System.out.println("\n");

        BeanInfo info3 = Introspector.getBeanInfo(MyArrayClass.class);
        PropertyDescriptor[] descriptors3 = info3.getPropertyDescriptors();
        for (int i = 0; i < descriptors3.length; i++) {
            System.out.println(descriptors3[i].getClass().getName() + ":" + descriptors3[i].getName());
        }

        System.out.println("\n");

        BeanInfo info4 = Introspector.getBeanInfo(MyIndexedArrayClass.class);
        PropertyDescriptor[] descriptors4 = info4.getPropertyDescriptors();
        for (int i = 0; i < descriptors4.length; i++) {
            System.out.println(descriptors4[i].getClass().getName() + ":" + descriptors4[i].getName());
        }

    }

    public class MyListClass {
        private List<String> myListClass = new ArrayList<String>();

        public List<String> getMyListClass() {
            return myListClass;
        }

        public void setMyListClass(List<String> myListClass) {
            this.myListClass = myListClass;
        }

    }

    public class MyIndexedListClass {
        private List<String> myIndexedListClass = new ArrayList<String>();

        public String getMyIndexedListClass(int index) {
            return myIndexedListClass.get(index);
        }

        public void setMyIndexedListClass(int index, String element) {
            this.myIndexedListClass.set(index, element);
        }

        public List<String> getMyIndexedListClass() {
            return myIndexedListClass;
        }

        public void setMyIndexedListClass(List<String> myIndexedListClass) {
            this.myIndexedListClass = myIndexedListClass;
        }

    }

    public class MyArrayClass {
        private String[] myArrayClass = new String[20];

        public String[] getMyArrayClass() {
            return myArrayClass;
        }

        public void setMyArrayClass(String[] myArrayClass) {
            this.myArrayClass = myArrayClass;
        }

    }

    public class MyIndexedArrayClass {
        private String[] myIndexedArrayClass = new String[20];

        public String getMyIndexedArrayClass(int index) {
            return myIndexedArrayClass[index];
        }

        public void setMyIndexedArrayClass(int index, String myValue) {
            this.myIndexedArrayClass[index] = myValue;
        }

        public String[] getMyIndexedArrayClass() {
            return myIndexedArrayClass;
        }

        public void setMyIndexedArrayClass(String[] myIndexedArrayClass) {
            this.myIndexedArrayClass = myIndexedArrayClass;
        }

    }
}

Here are the JDK 7 logs:

java.beans.PropertyDescriptor:class
java.beans.PropertyDescriptor:myListClass

java.beans.PropertyDescriptor:class
java.beans.IndexedPropertyDescriptor:myIndexedListClass

java.beans.PropertyDescriptor:class
java.beans.PropertyDescriptor:myArrayClass

java.beans.PropertyDescriptor:class
java.beans.IndexedPropertyDescriptor:myIndexedArrayClass

Here are the logs for JDK8:

java.beans.PropertyDescriptor:class
java.beans.PropertyDescriptor:myListClass

java.beans.PropertyDescriptor:class
java.beans.PropertyDescriptor:myIndexedListClass -> Here is the change

java.beans.PropertyDescriptor:class
java.beans.PropertyDescriptor:myArrayClass

java.beans.PropertyDescriptor:class
java.beans.IndexedPropertyDescriptor:myIndexedArrayClass

I have to use the JDK8 really soon but this is a blocking change because my app doesn't work anymore. I have this issue with all the classes that extend the Collection interface (List, Map, ...)

The code is used by the library commons-beanutils-1.8.0 from Apache Commons.

I'm looking for a solution, a workaround, to make my application work the same way as before, when using the JDK7, is there any solution? I can't use Array instead of List (because Array hasn't changed)

Here are the links to the official documentation:

JDK7: http://docs.oracle.com/javase/7/docs/api/java/beans/IndexedPropertyDescriptor.html

JDK8: http://docs.oracle.com/javase/8/docs/api/java/beans/IndexedPropertyDescriptor.html


EDIT : I found my solution, my problem was related with struts 1.3. I had to rename my indexed getter/setter in my ActionForm : http://www.coderanch.com/t/55172/Struts/Indexed-Properties

回答1:

Well, the specification clearly says that an IndexedPropertyDescriptor may have additional array based accessor methods, nothing else. That hasn’t changed. What you have here are conflicting property methods defining a simple List<String> typed property and and an indexed String property of the same name. The List based methods were never associated with the indexed property.

So what has changed is which of the conflicting properties makes it into the BeanInfo and which will be dropped. This behavior might be dependent on the unspecified order of a HashMap or such alike. There might be other factors as well. Thus, don’t see it as a Java 7 vs. Java 8 issue, but just an implementation dependent behavior which may also change between alternative Java 7 implementations.

There are two ways to resolve this problem. You may solve the conflict by renaming one of the properties:

public class MyIndexedListClass {
    private List<String> myIndexedListClass = new ArrayList<String>();

    public String getMyIndexedListClass(int index) {
        return myIndexedListClass.get(index);
    }

    public void setMyIndexedListClass(int index, String element) {
        this.myIndexedListClass.set(index, element);
    }

    public List<String> getMyIndexedListClassAsList() {
        return myIndexedListClass;
    }

    public void setMyIndexedListClassAsList(List<String> myIndexedListClass) {
        this.myIndexedListClass = myIndexedListClass;
    }
}

Then, all Java versions will behave the same, but it has the side effect of now seeing the two different properties as differently named properties.


The alternative is to keep the methods as is but explicitly elide the List based methods from the property descriptor recognition. In other words, make the behavior, which happened in one implementation and seems to be your desired one, explicit.

public class MyIndexedListClass {
    private List<String> myIndexedListClass = new ArrayList<String>();

    public String getMyIndexedListClass(int index) {
        return myIndexedListClass.get(index);
    }
    public void setMyIndexedListClass(int index, String element) {
        this.myIndexedListClass.set(index, element);
    }
    public List<String> getMyIndexedListClass() {
        return myIndexedListClass;
    }
    public void setMyIndexedListClass(List<String> myIndexedListClass) {
        this.myIndexedListClass = myIndexedListClass;
    }
}
static // in your example all classes are inner classes
public class MyIndexedListClassBeanInfo extends SimpleBeanInfo {
  private PropertyDescriptor[] properties;

  public MyIndexedListClassBeanInfo() throws IntrospectionException {
    PropertyDescriptor[] p=Introspector.getBeanInfo(MyIndexedListClass.class,
        Introspector.IGNORE_IMMEDIATE_BEANINFO).getPropertyDescriptors();
    ArrayList<PropertyDescriptor> list=new ArrayList<>(p.length+1);
    for(PropertyDescriptor d: p)
      if(!d.getName().equals("myIndexedListClass")) list.add(d);
    list.add(new IndexedPropertyDescriptor("myIndexedListClass",
        MyIndexedListClass.class, null, null,
        "getMyIndexedListClass", "setMyIndexedListClass"));
    properties=list.toArray(new PropertyDescriptor[list.size()]);
  }

  @Override
  public PropertyDescriptor[] getPropertyDescriptors() {
      return properties;
  }
}