commons.beanutils包中BeanUtils类map2bean问题

首先说一下遇到的问题:准备用map2bean工具,选择了BeanUtils中的BeanUtils.populate(bean, map);方法。
下面是我的bean。个别字段不是驼峰格式,因为MAP是别人提供的,其实把Map中属性名称改成驼峰样子我遇到的问题就消失了map2bean正常工作,但是我得知道他为啥都是大写字母开头最后一个变量咋正常呢。
public class ExtendedPropertie implements Serializable{


private static final long serialVersionUID = 1625175373011702860L;
private String PlateStoreFlag;
private String copilot_safebelt;
private String department;
private String ICE2MQ_Receive;
public String getPlateStoreFlag() {
return PlateStoreFlag;
}
public void setPlateStoreFlag(String plateStoreFlag) {
PlateStoreFlag = plateStoreFlag;
}
public String getCopilot_safebelt() {
return copilot_safebelt;
}
public void setCopilot_safebelt(String copilot_safebelt) {
this.copilot_safebelt = copilot_safebelt;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public String getICE2MQ_Receive() {
return ICE2MQ_Receive;
}
public void setICE2MQ_Receive(String iCE2MQ_Receive) {
ICE2MQ_Receive = iCE2MQ_Receive;
}
}
遇到的问题map2bean后PlateStoreFlag为null,其他正常。打断点测试。
BeanUtils.populate(bean, map);->org.apache.commons.beanutils.populate--->org.apache.commons.beanutils.BeanUtilsBean.getInstance().populate(bean, properties);
--->org.apache.commons.beanutils.BeanUtilsBean.setProperty(bean, name, entry.getValue());
此方法中包含这样一行
 descriptor =
                    getPropertyUtils().getPropertyDescriptor(target, name);
descriptor 中有方法的描述,类变量的描述等,接着追根溯源。
接着找下去看到这个了
final BeanIntrospectionData data = getIntrospectionData(bean.getClass());


BeanIntrospectionData 类中有这么两个成员变量,一个是目标类的成员变量描述,一个是可写的方法就是set方法
/** An array with property descriptors for the managed bean class. */
    private final PropertyDescriptor[] descriptors;




    /** A map for remembering the write method names for properties. */
    private final Map<String, String> writeMethodNames;


接着看代码:
 private BeanIntrospectionData getIntrospectionData(final Class<?> beanClass) {
        if (beanClass == null) {
            throw new IllegalArgumentException("No bean class specified");
        }
        // Look up any cached information for this bean class
        BeanIntrospectionData data = descriptorsCache.get(beanClass);
        if (data == null) {
            data = fetchIntrospectionData(beanClass);
            descriptorsCache.put(beanClass, data);
        }
        return data;
    }
可以看出来先从缓存中取BeanIntrospectionData  如果没有 fetchIntrospectionData(beanClass)
接着往下走,
private BeanIntrospectionData fetchIntrospectionData(final Class<?> beanClass) {
        final DefaultIntrospectionContext ictx = new DefaultIntrospectionContext(beanClass);


//    /** The list with BeanIntrospector objects. */
//    private final List<BeanIntrospector> introspectors;


        for (final BeanIntrospector bi : introspectors) {
            try {
                bi.introspect(ictx);
            } catch (final IntrospectionException iex) {
                log.error("Exception during introspection", iex);
            }
        }




        return new BeanIntrospectionData(ictx.getPropertyDescriptors());
    }
打断点监测发现问题发生在这一行 bi.introspect(ictx);这一行之后问题就出现了。查看introspect这个方法在interface BeanIntrospector中有三个实现,
进入发现是这个类class DefaultBeanIntrospector implements BeanIntrospector
此类中的方法introspect中有这么几行
BeanInfo beanInfo = null;
beanInfo = Introspector.getBeanInfo(icontext.getTargetClass());
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
BeanInfo ,Introspector是
java.beans中的类


在eclipse中调试jdk的源码,(关联源码的帮助:https://jingyan.baidu.com/album/915fc41483a39551394b20bb.html?picindex=1)接着找原因
Introspector类中的方法:public static BeanInfo getBeanInfo(Class<?> beanClass)
进入这个方法了beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo(); 
先看Introspector实例化的过程,先找单参数构造器 beanInfo,然后是父类beaninfo,见一下方法
superBeanInfo = getBeanInfo(superClass, stopClass, newFlags);
继续深入方法
  bi = getBeanInfo(beanClass);
经过这个方法bi中终于有东西了,但是那是父类object的bean描述,至此红色代码的实例化结束
继续往下走该是自定的bean中的东西了吧,进入红色代码的getBeanInfo方法。
private BeanInfo getBeanInfo() throws IntrospectionException {




        // the evaluation order here is import, as we evaluate the
        // event sets and locate PropertyChangeListeners before we
        // look for properties.
        BeanDescriptor bd = getTargetBeanDescriptor();
        MethodDescriptor mds[] = getTargetMethodInfo();
        EventSetDescriptor esds[] = getTargetEventInfo();
        PropertyDescriptor pds[] = getTargetPropertyInfo();




        int defaultEvent = getTargetDefaultEventIndex();
        int defaultProperty = getTargetDefaultPropertyIndex();




        return new GenericBeanInfo(bd, esds, defaultEvent, pds,
                        defaultProperty, mds, explicitBeanInfo);
    }


都是干货,终于找到源头了。进getTargetPropertyInfo,我给代码加注释,看注释吧
    private PropertyDescriptor[] getTargetPropertyInfo() {




        // Check if the bean has its own BeanInfo that will provide
        // explicit information.
        PropertyDescriptor[] explicitProperties = null;
        if (explicitBeanInfo != null) { //explicitBeanInfo 是null
            explicitProperties = getPropertyDescriptors(this.explicitBeanInfo);
        }




        if (explicitProperties == null && superBeanInfo != null) {
            // We have no explicit BeanInfo properties.  Check with our parent.
            addPropertyDescriptors(getPropertyDescriptors(this.superBeanInfo)); //把父类的beaninfo中的PropertyDescriptors信息加入进来
        }




        for (int i = 0; i < additionalBeanInfo.length; i++) {
            addPropertyDescriptors(additionalBeanInfo[i].getPropertyDescriptors());
        }


//三大类  beaninfo处理完了。  private BeanInfo explicitBeanInfo;
 //   private BeanInfo superBeanInfo;
  //  private BeanInfo additionalBeanInfo[];


        if (explicitProperties != null) {//试图系通过部分 参数的构造方法找到properties
            // Add the explicit BeanInfo data to our results.
            addPropertyDescriptors(explicitProperties);




        } else {//没办法了,出绝招了




            // Apply some reflection to the current class.


   //拿到pulic 方法
            // First get an array of all the public methods at this level
            Method methodList[] = getPublicDeclaredMethods(beanClass);




            // Now analyze each method.
            for (int i = 0; i < methodList.length; i++) {
                Method method = methodList[i];
                if (method == null) {
                    continue;
                }
                // skip static methods.
                int mods = method.getModifiers();
                if (Modifier.isStatic(mods)) {//去掉静态方法
                    continue;
                }
                String name = method.getName();
                Class argTypes[] = method.getParameterTypes();
                Class resultType = method.getReturnType();
                int argCount = argTypes.length;
                PropertyDescriptor pd = null;


//去掉长度小于三的,get,set加方法名大于3,去掉is开头的方法
                if (name.length() <= 3 && !name.startsWith(IS_PREFIX)) {
                    // Optimization. Don't bother with invalid propertyNames.
                    continue;
                }




                try {


//参数为零 get方法
                    if (argCount == 0) {
                        if (name.startsWith(GET_PREFIX)) {//get开头的方法
                            // Simple getter
                            pd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null);    //最关键的一步了,mark一下
                        } else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {
                            // Boolean getter
                            pd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null);
                        }
                    } else if (argCount == 1) {
                        if (int.class.equals(argTypes[0]) && name.startsWith(GET_PREFIX)) {
                            pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, method, null);
                        } else if (void.class.equals(resultType) && name.startsWith(SET_PREFIX)) {
                            // Simple setter
                            pd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method);
                            if (throwsException(method, PropertyVetoException.class)) {
                                pd.setConstrained(true);
                            }
                        }
                    } else if (argCount == 2) {
                            if (void.class.equals(resultType) && int.class.equals(argTypes[0]) && name.startsWith(SET_PREFIX)) {
                            pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, null, method);
                            if (throwsException(method, PropertyVetoException.class)) {
                                pd.setConstrained(true);
                            }
                        }
                    }
                } catch (IntrospectionException ex) {
                    // This happens if a PropertyDescriptor or IndexedPropertyDescriptor
                    // constructor fins that the method violates details of the deisgn
                    // pattern, e.g. by having an empty name, or a getter returning
                    // void , or whatever.
                    pd = null;
                }




                if (pd != null) {
                    // If this class or one of its base classes is a PropertyChange
                    // source, then we assume that any properties we discover are "bound".
                    if (propertyChangeSource) {
                        pd.setBound(true);
                    }
                    addPropertyDescriptor(pd);
                }
            }
        }
        processPropertyDescriptors();




        // Allocate and populate the result array.
        PropertyDescriptor result[] = new PropertyDescriptor[properties.size()];
        result = (PropertyDescriptor[])properties.values().toArray(result);




        // Set the default index.
        if (defaultPropertyName != null) {
            for (int i = 0; i < result.length; i++) {
                if (defaultPropertyName.equals(result[i].getName())) {
                    defaultPropertyIndex = i;
                }
            }
        }




        return result;
    }


   这行 pd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null);    //最关键的一步了,mark一下,继续深入
//传了三个参数,class,get方法去掉get,get Method 对象,null
 PropertyDescriptor(Class<?> bean, String base, Method read, Method write) throws IntrospectionException {
        if (bean == null) {
            throw new IntrospectionException("Target Bean class is null");
        }
        setClass0(bean);
        setName(Introspector.decapitalize(base));
        setReadMethod(read);
        setWriteMethod(write);
        this.baseName = base;
    }


        setName(Introspector.decapitalize(base));见真相
 /**
     * Utility method to take a string and convert it to normal Java variable
     * name capitalization.  This normally means converting the first
     * character from upper case to lower case, but in the (unusual) special
     * case when there is more than one character and both the first and
     * second characters are upper case, we leave it alone.
     * <p>
     * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
     * as "URL".
     *
     * @param  name The string to be decapitalized.
     * @return  The decapitalized version of the string.
     */
    public static String decapitalize(String name) {
        if (name == null || name.length() == 0) {
            return name;
        }
        if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                        Character.isUpperCase(name.charAt(0))){
            return name;
        }
        char chars[] = name.toCharArray();
        chars[0] = Character.toLowerCase(chars[0]);
        return new String(chars);
    }
翻译下这个方法的注释。从公用的方法截取字段作为变量名。截取的字段第一个大写就把他变成小写,但是特殊的字段不止第一个字母大写,第二个也大写,所以我们就不管他了。
这也解释了PlateStoreFlag不正常,ICE2MQ_Receive正常的原因的。
至于为啥不通过Field[] fields = class1.getFields();获得feild,因为变量是私有变量。
我来解释下整个过程。
首先根据Map中的key和value还有要转的Bean,
去获取PropertyDescriptor,先根据bean拿到BeanIntrospectionData,要获得BeanIntrospectionData 先拿到BeanInfo,拿到BeanInfo的时候已经把大小写转化做完了,保存在PropertyDescriptor。然后根据name(map中的key)去data.getDescriptor(name)获取,当然获取不到的,因为data中的name 肯定是小写开头的。而map中给的是大写开头的。正常情况下拿到PropertyDescriptor,然后根据PropertyDescriptor获得数据类型然后 将Map中的value转换成合适的类型。然后进入往变量写数据的流程,要写数据就要拿到set方法。然后再根据bean获得PropertyDescriptor ,因为第一次有缓存所以去缓存中取。然后获得set方法最后执行 invokeMethod(writeMethod, bean, values);写入数据













评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值