JRE 1.8仍然符合JavaBean规范关于IndexedPropertyDescriptor吗?

5
这个问题看起来有些奇怪,但我们在检索JavaBean的PropertyDescriptor时遇到了一种奇怪的行为。以下是使用1.6、1.7和1.8编译的简单代码的执行结果。
Java 1.6 执行结果:
java.beans.PropertyDescriptor@4ddc1428 <- 不重要 java.beans.IndexedPropertyDescriptor@7174807e <- 是的,我有一个索引属性 Java 1.7 执行结果:
java.beans.PropertyDescriptor[name=class; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()] <- 不重要 java.beans.IndexedPropertyDescriptor[name=values; indexedPropertyType=class java.lang.String; indexedReadMethod=public java.lang.String JavaBean.getValues(int)] <- 是的,我有一个索引属性 Java 1.8 执行结果:
java.beans.PropertyDescriptor[name=class; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()] <- 不重要 java.beans.PropertyDescriptor[name=values; propertyType=interface java.util.List; readMethod=public java.util.List JavaBean.getValues()] <- 啊!这不再是一个索引属性了! 为什么会发生变化?
JavaBean规范规定访问具有索引的属性时,并没有强制要求使用数组作为索引属性的容器。我错了吗?
我读了规范,第8.3.3章谈到了索引属性的设计模式,而不是严格规则。
如何使以前的行为重新出现而不需要重构整个应用程序?< 旧应用程序,需要修改很多代码等等...
谢谢回答,
JavaBean类
import java.util.ArrayList;  
import java.util.List;  


public class JavaBean {  


  private List<String> values = new ArrayList<String>();  


  public String getValues(int index) {  
  return this.values.get(index);  
  }  


  public List<String> getValues() {  
  return this.values;  
  }  
}  

主类

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;

public class Test {
    public static void main(String[] args) throws IntrospectionException {
         PropertyDescriptor[] descs =
         Introspector.getBeanInfo(JavaBean.class).getPropertyDescriptors();
         for (PropertyDescriptor pd : descs) {
         System.out.println(pd);
         }
    }
}
2个回答

2
从JavaBeans 1.01规范第7.2节“索引属性”中可以看出:一个组件也可以将索引属性公开为单个数组值。
第8.3节描述了设计模式,其中内省在没有显式BeanInfo的情况下进行识别。第8.3.3节指出,只有数组属性会触发自动识别索引属性。
你在技术上是正确的;不使用数组并不是强制性的。但是,如果不使用数组,则规范要求您提供自己的BeanInfo将该属性公开为索引属性。
所以,对于您问题的标题:是的,Java 1.8符合JavaBean规范。
我不确定为什么List属性曾经受到支持。也许未来的JavaBeans规范将会支持它们,但现在已被撤回。
至于你的最后一个问题:我认为你将不得不为每个具有List属性的类创建一个BeanInfo类。我期望你可以创建一个通用的超类以使其更容易,例如:
public abstract class ListRecognizingBeanInfo
extends SimpleBeanInfo {

    private final BeanDescriptor beanDesc;
    private final PropertyDescriptor[] propDesc;

    protected ListRecognizingBeanInfo(Class<?> beanClass)
    throws IntrospectionException {
        beanDesc = new BeanDescriptor(beanClass);

        List<PropertyDescriptor> desc = new ArrayList<>();

        for (Method method : beanClass.getMethods()) {
            int modifiers = method.getModifiers();
            Class<?> type = method.getReturnType();

            if (Modifier.isPublic(modifiers) &&
                !Modifier.isStatic(modifiers) &&
                !type.equals(Void.TYPE) &&
                method.getParameterCount() == 0) {

                String name = method.getName();
                String remainder;
                if (name.startsWith("get")) {
                    remainder = name.substring(3);
                } else if (name.startsWith("is") &&
                           type.equals(Boolean.TYPE)) {
                    remainder = name.substring(2);
                } else {
                    continue;
                }

                if (remainder.isEmpty()) {
                    continue;
                }

                String propName = Introspector.decapitalize(remainder);

                Method writeMethod = null;
                Method possibleWriteMethod =
                    findMethod(beanClass, "set" + remainder, type);
                if (possibleWriteMethod != null &&
                    possibleWriteMethod.getReturnType().equals(Void.TYPE)) {

                    writeMethod = possibleWriteMethod;
                }

                Class<?> componentType = null;
                if (type.isArray()) {
                    componentType = type.getComponentType();
                } else {
                    Type genType = method.getGenericReturnType();
                    if (genType instanceof ParameterizedType) {
                        ParameterizedType p = (ParameterizedType) genType;
                        if (p.getRawType().equals(List.class)) {
                            Type[] argTypes = p.getActualTypeArguments();
                            if (argTypes[0] instanceof Class) {
                                componentType = (Class<?>) argTypes[0];
                            }
                        }
                    }
                }

                Method indexedReadMethod = null;
                Method indexedWriteMethod = null;

                if (componentType != null) {
                    Method possibleReadMethod =
                        findMethod(beanClass, name, Integer.TYPE);
                    Class<?> idxType = possibleReadMethod.getReturnType();
                    if (idxType.equals(componentType)) {
                        indexedReadMethod = possibleReadMethod;
                    }

                    if (writeMethod != null) {
                        possibleWriteMethod =
                            findMethod(beanClass, writeMethod.getName(),
                                Integer.TYPE, componentType);
                        if (possibleWriteMethod != null &&
                            possibleWriteMethod.getReturnType().equals(
                                Void.TYPE)) {

                            indexedWriteMethod = possibleWriteMethod;
                        }
                    }
                }

                if (indexedReadMethod != null) {
                    desc.add(new IndexedPropertyDescriptor(propName,
                        method, writeMethod,
                        indexedReadMethod, indexedWriteMethod));
                } else {
                    desc.add(new PropertyDescriptor(propName,
                        method, writeMethod));
                }
            }
        }

        propDesc = desc.toArray(new PropertyDescriptor[0]);
    }

    private static Method findMethod(Class<?> cls,
                                     String name,
                                     Class<?>... paramTypes) {
        try {
            Method method = cls.getMethod(name, paramTypes);
            int modifiers = method.getModifiers();
            if (Modifier.isPublic(modifiers) &&
                !Modifier.isStatic(modifiers)) {

                return method;
            }
        } catch (NoSuchMethodException e) {
        }

        return null;
    }

    @Override
    public BeanDescriptor getBeanDescriptor() {
        return beanDesc;
    }

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

嗯...我预料到会得到这种回答。我仍然不明白为什么行为会改变成更严格的实现方式。不管怎样,你的答案很好。谢谢。由于我们正在使用commons-beanutils,我更喜欢通过使用BeanIntrospector来扩展此API的功能,它可以在内省bean时检测到我自己定义的IndexedProperty。 - Fabien BUSSINGER

2
我遇到了同样的问题。我试图从JSP保存StartDate和EndDate作为List,但是它们没有被保存并且值被清除了。在我的项目中,有开始日期和结束日期字段。我调试了BeanUtilsBean,然后发现这些字段没有writeMethod。我在我的类中添加了另一个setter方法,它起作用了。
java.beans.PropertyDescriptor[name=startDateStrings; propertyType=interface java.util.List; readMethod=public java.util.List com.webapp.tradingpartners.TradingPartnerNewForm.getStartDateStrings()]
这是JavaBeans规范的问题,它只允许void setters。如果你想要返回值的setter,那么你就不能符合JavaBeans的兼容性,Lombok也无能为力。
理论上,你可以生成两个setter,但是你必须以不同的方式调用它们,每个字段有两个setter太糟糕了。
<cc:dateInput property='<%= "startDateStrings[" + row + "]" %>' onchange="setPropertyChangedFlag()"/>                     
<cc:dateInput property='<%= "endDateStrings[" + row + "]" %>' onchange="setPropertyChangedFlag()"/>                     
public List<String> getStartDateStrings() {
  return startDateStrings;
}
public String getStartDateStrings(int index) {
    return startDateStrings.get(index);
  }
public void setStartDateStrings(int index, String value) {
    startDateStrings.set(index, value);
  }
public List<String> getEndDateStrings() {
    return endDateStrings;
  }
public String getEndDateStrings(int index) {
    return endDateStrings.get(index);
  }
public void setEndDateStrings(int index, String value) {
    endDateStrings.set(index, value);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接