All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.feilong.core.bean.BeanUtil Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.0.8
Show newest version
/*
 * Copyright (C) 2008 feilong
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.feilong.core.bean;

import static com.feilong.core.Validator.isNotNullOrEmpty;
import static com.feilong.core.Validator.isNullOrEmpty;
import static com.feilong.core.util.MapUtil.newHashMap;
import static java.util.Collections.emptyMap;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.DynaBean;

import com.feilong.core.Validate;
import com.feilong.lib.beanutils.BeanUtils;
import com.feilong.lib.beanutils.LazyDynaBean;
import com.feilong.lib.lang3.reflect.FieldUtils;
import com.feilong.tools.slf4j.Slf4jUtil;

/**
 * 对 {@link com.feilong.lib.beanutils.BeanUtils}的再次封装.
 * 
 * 

说明:

*
*
    *
  1. 目的是将原来的 checkedException 异常 转换成 {@link BeanOperationException}
  2. *
*
* * *

{@link com.feilong.lib.beanutils.PropertyUtils}与 {@link com.feilong.lib.beanutils.BeanUtils}区别:

* *
* *
 * BeanUtils.setProperty(pt1, "x", "9"); // 这里的9是String类型
 * PropertyUtils.setProperty(pt1, "x", 9); // 这里的是int类型
 * // 这两个类BeanUtils和PropertyUtils,前者能自动将int类型转化,后者不能
 * 
* *

* {@link com.feilong.lib.beanutils.PropertyUtils}类和 {@link com.feilong.lib.beanutils.BeanUtils}类很多的方法在参数上都是相同的,但返回值不同. 
* BeanUtils着重于"Bean",返回值通常是String,
* 而PropertyUtils着重于属性,它的返回值通常是Object.  *

*
* *

关于propertyName

* *
* getProperty和setProperty,它们都只有2个参数,第一个是JavaBean对象,第二个是要操作的属性名. * *
 * Company company = new Company();
 * company.setName("Simple");
 * 
* *
*
Simple类型(简单类型,如String Int)
*
* 对于Simple类型,参数二直接是属性名即可 * *
 * LOGGER.debug(BeanUtils.getProperty(company, "name"));
 * 
* *
* *
Map类型
*
* 对于Map类型,则需要以"属性名(key值)"的形式 * *
 * LOGGER.debug(BeanUtils.getProperty(company, "address (A2)"));
 * 
 * Map{@code } am = newHashMap();
 * am.put("1", "234-222-1222211");
 * am.put("2", "021-086-1232323");
 * 
 * BeanUtils.setProperty(company, "telephone", am);
 * LOGGER.debug(BeanUtils.getProperty(company, "telephone (2)"));
 * 
* *
* *
索引类型(Indexed),如 数组 arrayList
*
* 对于Indexed,则为"属性名[索引值]",注意这里对于ArrayList和数组都可以用一样的方式进行操作. * *
 * LOGGER.debug(BeanUtils.getProperty(company, "otherInfo[2]"));
 * BeanUtils.setProperty(company, "product[1]", "NOTES SERVER");
 * LOGGER.debug(BeanUtils.getProperty(company, "product[1]"));
 * 
* *
* *
组合/嵌套(Nested)
*
* 当然这3种类也可以组合使用啦! * *
 * LOGGER.debug(BeanUtils.getProperty(company, "employee[1].name"));
 * 
* *
*
*
* *

关于 {@link BeanUtils#copyProperty(Object, String, Object) copyProperty} 和 {@link BeanUtils#setProperty(Object, String, Object) * setProperty}的区别:

* *
* *
 * 两者功能相似,区别点在于:
 * copyProperty 不支持目标bean是索引类型,但是支持bean有索引类型的setter方法
 * copyProperty 不支持目标bean是Map类型,但是支持bean有Map类型的setter方法
 * 
 * 如果我们只是为bean的属性赋值的话,使用{@link BeanUtils#copyProperty(Object, String, Object)}就可以了;
 * 而{@link BeanUtils#setProperty(Object, String, Object)}方法是实现  {@link BeanUtils#populate(Object,Map)}机制的基础,也就是说如果我们需要自定义实现populate()方法,那么我们可以override {@link BeanUtils#setProperty(Object, String, Object)}方法.
 * 所以,做为一般的日常使用,{@link BeanUtils#setProperty(Object, String, Object)}方法是不推荐使用的.
 * 
* *
* * @author feilong * * @see com.feilong.core.bean.PropertyUtil * * @see java.beans.BeanInfo * @see java.beans.PropertyDescriptor * @see java.beans.MethodDescriptor * * @see com.feilong.lib.beanutils.BeanUtils * @see com.feilong.lib.beanutils.Converter * @see com.feilong.lib.beanutils.converters.DateConverter * @see com.feilong.lib.beanutils.converters.DateTimeConverter * @see com.feilong.lib.beanutils.converters.AbstractConverter * * @see com.feilong.lib.beanutils.ConvertUtils#register(com.feilong.lib.beanutils.Converter, Class) * * @see com.feilong.lib.beanutils.ConvertUtilsBean#registerPrimitives(boolean) * @see com.feilong.lib.beanutils.ConvertUtilsBean#registerStandard(boolean, boolean) * @see com.feilong.lib.beanutils.ConvertUtilsBean#registerOther(boolean) * @see com.feilong.lib.beanutils.ConvertUtilsBean#registerArrays(boolean, int) * * @see "org.springframework.beans.BeanUtils" * * @since 1.0.0 */ public final class BeanUtil{ /** Don't let anyone instantiate this class. */ private BeanUtil(){ //AssertionError不是必须的. 但它可以避免不小心在类的内部调用构造器. 保证该类在任何情况下都不会被实例化. //see 《Effective Java》 2nd throw new AssertionError("No " + getClass().getName() + " instances for you!"); } //--------------------------------------------------------------- static{ //初始化注册器. ConvertUtil.registerStandardDefaultNull(); } //--------------------------------------------------------------- // [start] copyProperties /** * 将 fromObj 中的全部或者一组属性 includePropertyNames 的值,复制到 toObj 对象中. * *

* 如果需要copy的两个对象属性的类型一样,那么调用此方法会有性能消耗,强烈建议使用 * {@link PropertyUtil#copyProperties(Object, Object, String...)} *

* *

注意:

* *
*
    *
  1. 这种copy都是 浅拷贝,复制后的2个Bean的同一个属性可能拥有同一个对象的ref,在使用时要小心,特别是对于属性为自定义类的情况 .
  2. *
  3. 此方法调用了 {@link BeanUtils#copyProperties(Object, Object)},会自动进行{@code Object--->String--->Object}类型转换
  4. *
  5. 如果指定了includePropertyNames,会调用 {@link #getProperty(Object, String)},在自动进行{@code Object--->String} * 类型转换过程中,如果发现值是数组,只会取第一个元素重新构造数组转到 toObj中,规则参见 {@link ConvertUtil#toString(Object)}
  6. *
  7. 不支持toObj是map类型,从{@link com.feilong.lib.beanutils.BeanUtilsBean#copyProperties(Object, Object)}源码可以看出, * fromObj可以是map *
  8. *
*
* *

使用示例:

* *
* *
     * 例如两个pojo: user和userForm 都含有字段"enterpriseName","linkMan","phone"
     * 
     * 通常写法:
     * .....
     * user.setEnterpriseName(userForm.getEnterpriseName());
     * user.setLinkMan(userForm.getLinkMan());
     * user.setPhone(userForm.getPhone());
     * ......
     * 
     * 此时,可以使用
     * BeanUtil.copyProperties(user,userForm,"enterpriseName","linkMan","phone");
     * 
* *
* *

注册自定义 {@link com.feilong.lib.beanutils.Converter}:

* *
*

* 如果有 * {@link java.util.Date}类型的需要copy,那么需要先使用{@link com.feilong.lib.beanutils.ConvertUtils#register(com.feilong.lib.beanutils.Converter, Class)}方法:
* * ConvertUtils.register(new DateLocaleConverter(Locale.US, DatePattern.TO_STRING_STYLE),Date.class); *

*
* *

{@link BeanUtils#copyProperties(Object, Object)}与 * {@link com.feilong.lib.beanutils.PropertyUtils#copyProperties(Object, Object)}区别

* *
*
    *
  • {@link BeanUtils#copyProperties(Object, Object)} 提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,而 * {@link com.feilong.lib.beanutils.PropertyUtils#copyProperties(Object, Object)}不支持这个功能,但是速度会更快一些.
  • *
  • commons-beanutils v1.9.0以前的版本 BeanUtils不允许对象的属性值为 null,PropertyUtils可以拷贝属性值 null的对象.
    * (注:commons-beanutils v1.9.0+修复了这个情况,BeanUtilsBean.copyProperties() no longer throws a ConversionException for null properties * of certain data types),具体信息,可以参阅commons-beanutils的 * RELEASE-NOTES.txt
  • *
*
* *

相比较直接调用 {@link BeanUtils#copyProperties(Object, Object)}的优点:

*
*
    *
  1. 将 checkedException 异常转成了 {@link BeanOperationException} RuntimeException,因为通常copy的时候出现了checkedException,也是普普通通记录下log,没有更好的处理方式 *
  2. *
  3. 支持 includePropertyNames 参数,允许针对性copy 个别属性
  4. *
  5. 更多,更容易理解的的javadoc
  6. *
*
* * @param toObj * 目标对象 * @param fromObj * 原始对象 * @param includePropertyNames * 包含的属性名字数组,(can be nested/indexed/mapped/combo)
*
    *
  1. 如果是null或者empty,那么直接调用 {@link BeanUtils#copyProperties(Object, Object)},否则循环调用{@link #getProperty(Object, String)} 再 * {@link #setProperty(Object, String, Object)}到 toObj对象中
  2. *
  3. 如果传入的includePropertyNames,含有 fromObj没有的属性名字,将会抛出异常
  4. *
  5. 如果传入的includePropertyNames,含有 fromObj有,但是 toObj没有的属性名字,可以正常运行(跳过该属性设置),see * {@link com.feilong.lib.beanutils.BeanUtilsBean#copyProperty(Object, String, Object)} Line391
  6. *
* @throws NullPointerException * 如果 toObj 是null,或者 fromObj 是null * @throws BeanOperationException * 其他调用api有任何异常,转成{@link BeanOperationException}返回 * @see com.feilong.lib.beanutils.BeanUtilsBean#copyProperties(Object, Object) * @see Bean复制的几种框架性能比较(Apache BeanUtils、PropertyUtils,Spring * BeanUtils,Cglib BeanCopier) */ //XXX add excludePropertyNames support public static void copyProperties(Object toObj,Object fromObj,String...includePropertyNames){ Validate.notNull(toObj, "toObj [destination bean] not specified!"); Validate.notNull(fromObj, "fromObj [origin bean] not specified!"); //--------------------------------------------------------------- if (isNullOrEmpty(includePropertyNames)){ try{ BeanUtils.copyProperties(toObj, fromObj); return; }catch (Exception e){ String pattern = "copyProperties exception,toObj:[{}],fromObj:[{}],includePropertyNames:[{}]"; throw new BeanOperationException(Slf4jUtil.format(pattern, toObj, fromObj, includePropertyNames), e); } } //--------------------------------------------------------------- for (String propertyName : includePropertyNames){ String value = getProperty(fromObj, propertyName); setProperty(toObj, propertyName, value); } } // [end] //--------------------------------------------------------------- // [start] setProperty /** * 使用 {@link BeanUtils#setProperty(Object, String, Object)} 来设置属性值(会进行类型转换). * *

* BeanUtils支持把所有类型的属性都作为字符串处理,在后台自动进行类型转换(字符串和真实类型的转换) *

* * @param bean * Bean on which setting is to be performed * @param propertyName * Property name (can be nested/indexed/mapped/combo),参见propertyName * @param value * Value to be set * @throws BeanOperationException * 有任何异常,转成{@link BeanOperationException}返回 * @see com.feilong.lib.beanutils.BeanUtils#setProperty(Object, String, Object) * @see com.feilong.lib.beanutils.BeanUtilsBean#setProperty(Object, String, Object) * @see "org.apache.commons.beanutils.PropertyUtils#setProperty(Object, String, Object)" * @see com.feilong.core.bean.PropertyUtil#setProperty(Object, String, Object) */ private static void setProperty(Object bean,String propertyName,Object value){ try{ BeanUtils.setProperty(bean, propertyName, value); }catch (Exception e){ String pattern = "setProperty exception,bean:[{}],propertyName:[{}],value:[{}]"; throw new BeanOperationException(Slf4jUtil.format(pattern, bean, propertyName, value), e); } } // [end] //--------------------------------------------------------------- // [start] getProperty /** * 使用 {@link BeanUtils#getProperty(Object, String)} 类从对象中取得属性值,不care值原本是什么类型,统统转成 {@link String}返回. * *

* 值转成字符串的规则,参见 {@link ConvertUtil#toString(Object)},比如如果发现值是数组,只会取第一个元素重新构造数组转到toObj中 *

* * @param bean * bean * @param propertyName * 属性名称 (can be nested/indexed/mapped/combo),参见 propertyName * @return 使用{@link BeanUtils#getProperty(Object, String)} 从对象中取得属性值 * @throws NullPointerException * 如果 bean 是null,或者如果 propertyName 是null * @throws IllegalArgumentException * 如果 propertyName 是blank * @throws BeanOperationException * 在调用 {@link BeanUtils#getProperty(Object, String)}过程中有任何异常,转成{@link BeanOperationException}返回 * @see com.feilong.lib.beanutils.BeanUtils#getProperty(Object, String) * @see "org.apache.commons.beanutils.PropertyUtils#getProperty(Object, String)" * @see com.feilong.core.bean.PropertyUtil#getProperty(Object, String) * @since 1.9.0 change access to private */ private static String getProperty(Object bean,String propertyName){ Validate.notNull(bean, "bean can't be null!"); Validate.notBlank(propertyName, "propertyName can't be blank!"); //--------------------------------------------------------------- try{ return BeanUtils.getProperty(bean, propertyName); }catch (Exception e){ String pattern = "getProperty exception,bean:[{}],propertyName:[{}]"; throw new BeanOperationException(Slf4jUtil.format(pattern, bean, propertyName), e); } } // [end] //--------------------------------------------------------------- // [start] cloneBean /** * 调用{@link BeanUtils#cloneBean(Object)}. * *

注意:

*
* *
    *
  1. 这个方法通过默认构造函数建立一个bean的新实例,然后拷贝每一个属性到这个新的bean中,即使这个bean没有实现 {@link Cloneable}接口 .
  2. *
  3. 是为那些本身没有实现clone方法的类准备的
  4. *
  5. 在源码上看是调用了 getPropertyUtils().copyProperties(newBean, bean);最后实际上还是复制的引用,无法实现深clone
    * 但还是可以帮助我们减少工作量的,假如类的属性不是基础类型的话(即自定义类),可以先clone出那个自定义类,在把他付给新的类,覆盖原来类的引用 *
  6. *
  7. * 如果需要深度clone,可以使用 {@link com.feilong.lib.lang3.SerializationUtils#clone(java.io.Serializable) SerializationUtils.clone} * ,但是它的性能要慢很多倍
  8. *
  9. 由于内部实现是通过 {@link java.lang.Class#newInstance() Class.newInstance()}来构造新的对象,所以需要被clone的对象必须存在默认无参构造函数,否则会出现异常 * {@link java.lang.InstantiationException InstantiationException}
  10. *
  11. 目前无法clone list,总是返回empty list,参见 * BeanUtils.cloneBean with List is broken *
  12. *
*
* * @param * the generic type * @param bean * Bean to be cloned * @return the cloned bean (复制的引用 ,无法实现深clone)
* @throws NullPointerException * 如果 bean 是null * @throws BeanOperationException * 在调用api有任何异常,转成{@link BeanOperationException}返回 * @see com.feilong.lib.beanutils.BeanUtils#cloneBean(Object) * @see "org.apache.commons.beanutils.PropertyUtilsBean#copyProperties(Object, Object)" * @see com.feilong.lib.lang3.SerializationUtils#clone(java.io.Serializable) * @see com.feilong.lib.lang3.ObjectUtils#clone(Object) * @see com.feilong.lib.lang3.ObjectUtils#cloneIfPossible(Object) * @see BeanUtils.cloneBean with List is broken */ @SuppressWarnings("unchecked") public static T cloneBean(T bean){ Validate.notNull(bean, "bean can't be null!"); //--------------------------------------------------------------- try{ return (T) BeanUtils.cloneBean(bean); }catch (Exception e){ String message = Slf4jUtil.format("cloneBean exception,bean:[{}]]", bean); throw new BeanOperationException(message, e); } } // [end] //--------------------------------------------------------------- // [start] populate(填充) 把properties/map里面的值放入bean中 /** * 把properties/map里面的值 populate (填充)bean中. * *

说明:

*
*
    *
  1. 将Map{@code }中的以值(String或String[])转换到目标bean对应的属性中,Key是目标bean的属性名. 
  2. *
  3. apache的javadoc中,明确指明这个方法是为解析http请求参数特别定义和使用的,在正常使用中不推荐使用.推荐使用 {@link #copyProperties(Object, Object, String...)}方法
  4. *
  5. 底层方法原理 {@link com.feilong.lib.beanutils.BeanUtilsBean#populate(Object, Map)},循环map,调用 * {@link com.feilong.lib.beanutils.BeanUtilsBean#setProperty(Object, String, Object)}方法 ,一一对应设置到 * bean对象
  6. *
  7. 如果properties key中有bean中不存在的属性,那么该条数据自动忽略
  8. *
  9. 如果properties key中有null,那么该条数据自动忽略,see {@link com.feilong.lib.beanutils.BeanUtilsBean#populate(Object, Map)} line 817
  10. *
  11. bean可以是Map类型,不过转换之后的key和value都会是Object类型,而不是声明的类型,see * {@link com.feilong.lib.beanutils.BeanUtilsBean#setProperty(Object, String, Object)} line * 928
  12. *
*
* *

示例:

*
* *
     * User user = new User();
     * user.setId(5L);
     * 
     * Map{@code } properties = newHashMap();
     * properties.put("id", 8L);
     * 
     * BeanUtil.populate(user, properties);
     * LOGGER.info(JsonUtil.format(user));
     * 
* * 返回: * *
     * {
     * "id": 8,
     * }
     * 
* *
* *

示例:

* *
* *

* 场景:mail.properties配置文件数据直接转成 MailSenderConfig对象 *

* *

* 有以下的mail.properties配置文件信息: *

* *
     * mailServerHost=smtp.exmail.qq.com // this is 胡编乱造的 account
     * [email protected]  // this is 胡编乱造的 account
     * password=feilong  // this is 胡编乱造的 account
     * 
     * personal=feilong store
     * 
     * [email protected],[email protected]
     * [email protected],[email protected]
     * [email protected],[email protected]
     * 
     * subject=hello world
     * 
     * mailServerPort=25
     * 
     * isDebug=true
     * 
     * isNeedReturnReceipt=true
     * 
* *

* 有以下bean信息: *

* *
     * 
     * public class MailSenderConfig{
     * 
     *     //** 发送邮件的服务器的IP
     *     private String mailServerHost;
     * 
     *     //** 邮件服务的端口 默认25.
     *     private String mailServerPort = "25";
     * 
     *     //** 登录邮件发送服务器的用户名
     *     private String userName;
     * 
     *     //** 登录邮件发送服务器的密码
     *     private String password;
     * 
     *     //** 是否debug 输出.  
     *     private boolean isDebug = false;
     * 
     *     //** 是否需要回执, 默认不需要.
     *     private boolean isNeedReturnReceipt = false;
     * 
     *     //** 邮件发送者的地址.
     *     private String fromAddress;
     * 
     *     //** 个人名义.
     *     private String personal = "";
     * 
     *     //** 邮件多人接收地址.
     *     private String[] tos;
     * 
     *     //** 邮件多人接收地址(抄送).
     *     private String[] ccs;
     * 
     *     //** 邮件多人接收地址.
     *     private String[] bccs;
     * 
     *      //setter getter 略
     * }
     * 
     * 
* * * 此时你可以如此调用代码: * * *
     * MailSenderConfig mailSenderConfig = new MailSenderConfig();
     * 
     * ResourceBundle resourceBundle = ResourceBundleUtil.getResourceBundle(FileUtil.getFileInputStream("mail.properties"));
     * BeanUtil.populate(mailSenderConfig, ResourceBundleUtil.toMap(resourceBundle));
     * 
     * LOGGER.debug(JsonUtil.format(mailSenderConfig));
     * 
* * 返回: * *
    {
        "subject": "hello world",
        "mailServerHost": "smtp.exmail.qq.com",
        "bccs":         [
            "123456test2",
            "163.com",
            "123456test3",
            "163.cn"
        ],
        "ccs":         [
            "123456test",
            "163.com",
            "123456test1",
            "163.cn"
        ],
        "password": "******",
        "mailServerPort": "25",
        "content": "",
        "tos":         [
            "123456",
            "163.com",
            "1234567",
            "163.cn"
        ],
        "personal": "feilong store",
        "isNeedReturnReceipt": true,
        "fromAddress": "",
        "userName": "[email protected]",
        "isDebug": true
    }
     * 
* *

* 此时你会发现,上面的 tos 期望值是 ["[email protected]","[email protected]"],但是和你的期望值不符合,ccs和bccs 也是相同的情况
* 因为,{@link com.feilong.lib.beanutils.converters.ArrayConverter} 默认允许的字符 allowedChars 只有 '.', '-',其他都会被做成分隔符 *

* *

* 你可以如此这般: *

* *
     * MailSenderConfig mailSenderConfig = new MailSenderConfig();
     * 
     * ResourceBundle resourceBundle = ResourceBundleUtil.getResourceBundle(FileUtil.getFileInputStream("mail.properties"));
     * 
     * ArrayConverter arrayConverter = new ArrayConverter(String[].class, new StringConverter(), 2);
     * char[] allowedChars = { '@' };
     * arrayConverter.setAllowedChars(allowedChars);
     * ConvertUtils.register(arrayConverter, String[].class);
     * 
     * BeanUtil.populate(mailSenderConfig, ResourceBundleUtil.toMap(resourceBundle));
     * 
     * LOGGER.debug(JsonUtil.format(mailSenderConfig));
     * 
* * 返回: * *
    {
        "subject": "hello world",
        "mailServerHost": "smtp.exmail.qq.com",
        "bccs":         [
            "[email protected]",
            "[email protected]"
        ],
        "ccs":         [
            "[email protected]",
            "[email protected]"
        ],
        "password": "******",
        "mailServerPort": "25",
        "content": "",
        "tos":         [
            "[email protected]",
            "[email protected]"
        ],
        "personal": "feilong store",
        "isNeedReturnReceipt": true,
        "fromAddress": "",
        "userName": "[email protected]",
        "isDebug": true
    }
     * 
* *

* 如果你的配置文件的key和bean的属性不一致(比如大小写,有分隔符等情况),你可以使用 {@link #populateAliasBean(Object, Map)} *

* *
* * @param * the generic type * @param bean * JavaBean whose properties are being populated * @param properties * Map keyed by property name,with the corresponding (String or String[]) value(s) to be set * @return 如果properties key中有bean中不存在的属性,那么该条数据自动忽略
* 如果properties key中有null,那么该条数据自动忽略,see {@link com.feilong.lib.beanutils.BeanUtilsBean#populate(Object, Map)} line 817
* @throws NullPointerException * 如果 bean 是null,或者如果 properties 是null * @throws BeanOperationException * 在调用{@link BeanUtils#populate(Object, Map)}过程中有任何异常,转成{@link BeanOperationException}返回 * @see com.feilong.lib.beanutils.BeanUtils#populate(Object, Map) */ public static T populate(T bean,Map properties){ Validate.notNull(bean, "bean can't be null/empty!"); Validate.notNull(properties, "properties can't be null/empty!"); //--------------------------------------------------------------- try{ BeanUtils.populate(bean, properties); return bean; }catch (Exception e){ String message = Slf4jUtil.format("can't populate:[{}] to bean:{}", properties, bean); throw new BeanOperationException(message, e); } } //--------------------------------------------------------------- /** * 将 alias 和value 的map populate (填充)aliasBean. * *

背景:

* *
*

* {@link BeanUtil} 有标准的populate功能:{@link #populate(Object, Map)} ,但是要求 map的key 和 bean的属性名称必须是一一对应
*

* *

* 有很多情况,比如 map 的key是 "memcached.alivecheck" 这样的字符串(常见于properties 的配置文件),或者是大写的 "ALIVECHECK" 的字符串(常见于第三方接口 xml属性名称)
* 而我们的bean里面的属性名称是标准的 java bean 规范的名字,比如 "aliveCheck",这时就没有办法直接使用 {@link #populate(Object, Map)} 方法了
*

* *

* 你可以使用 {@link #populateAliasBean(Object, Map)}方法~~ *

*
* *

示例:

* *
* *

* 有以下aliasAndValueMap信息: *

* *
     * {
     * "memcached.alivecheck": "false",
     * "memcached.expiretime": "180",
     * "memcached.initconnection": "10",
     * "memcached.maintSleep": "30",
     * "memcached.maxconnection": "250",
     * "memcached.minconnection": "5",
     * "memcached.nagle": "false",
     * "memcached.poolname": "sidsock2",
     * "memcached.serverlist": "172.20.31.23:11211,172.20.31.22:11211",
     * "memcached.serverweight": "2",
     * "memcached.socketto": "3000"
     * }
     * 
     * 
* *

* 有以下aliasBean信息: *

* *
     * 
     * public class DangaMemCachedConfig{
     * 
     *     //** The serverlist.
     *     @Alias(name = "memcached.serverlist",sampleValue = "172.20.31.23:11211,172.20.31.22:11211")
     *     private String[] serverList;
     * 
     *     //@Alias(name = "memcached.poolname",sampleValue = "sidsock2")
     *     private String poolName;
     * 
     *     //** The expire time 单位分钟.
     *     @Alias(name = "memcached.expiretime",sampleValue = "180")
     *     private Integer expireTime;
     * 
     *     //** 权重. 
     *     @Alias(name = "memcached.serverweight",sampleValue = "2,1")
     *     private Integer[] weight;
     * 
     *     //** The init connection. 
     *     @Alias(name = "memcached.initconnection",sampleValue = "10")
     *     private Integer initConnection;
     * 
     *     //** The min connection.
     *     @Alias(name = "memcached.minconnection",sampleValue = "5")
     *     private Integer minConnection;
     * 
     *     //** The max connection. 
     *     @Alias(name = "memcached.maxconnection",sampleValue = "250")
     *     private Integer maxConnection;
     * 
     *     //** 设置主线程睡眠时间,每30秒苏醒一次,维持连接池大小.
     *     @Alias(name = "memcached.maintSleep",sampleValue = "30")
     *     private Integer maintSleep;
     * 
     *     //** 关闭套接字缓存. 
     *     @Alias(name = "memcached.nagle",sampleValue = "false")
     *     private Boolean nagle;
     * 
     *     //** 连接建立后的超时时间.
     *     @Alias(name = "memcached.socketto",sampleValue = "3000")
     *     private Integer socketTo;
     * 
     *     //** The alive check.
     *     @Alias(name = "memcached.alivecheck",sampleValue = "false")
     *     private Boolean aliveCheck;
     * 
     *     //setter getter 略
     * }
     * 
     * 
* * * 此时你可以如此调用代码: * * *
     * Map{@code } readPropertiesToMap = ResourceBundleUtil.toMap(ResourceBundleUtil.getResourceBundle("memcached"));
     * 
     * DangaMemCachedConfig dangaMemCachedConfig = new DangaMemCachedConfig();
     * BeanUtil.populateAliasBean(dangaMemCachedConfig, readPropertiesToMap);
     * LOGGER.debug(JsonUtil.format(dangaMemCachedConfig));
     * 
* * 返回: * *
    {
        "maxConnection": 250,
        "expireTime": 180,
        "serverList":         [
            "172.20.31.23",
            "11211",
            "172.20.31.22",
            "11211"
        ],
        "weight": [2],
        "nagle": false,
        "initConnection": 10,
        "aliveCheck": false,
        "poolName": "sidsock2",
        "maintSleep": 30,
        "socketTo": 3000,
        "minConnection": 5
    }
     * 
* *

* 此时你会发现,上面的 serverList 期望值是 ["172.20.31.23:11211","172.20.31.22:11211"],但是和你的期望值不符合,
* 因为, {@link com.feilong.lib.beanutils.converters.ArrayConverter} 默认允许的字符 allowedChars 只有 '.', '-',其他都会被做成分隔符 *

* *

* 你需要如此这般: *

* *
     * Map{@code } readPropertiesToMap = ResourceBundleUtil.toMap(ResourceBundleUtil.getResourceBundle("memcached"));
     * 
     * DangaMemCachedConfig dangaMemCachedConfig = new DangaMemCachedConfig();
     * 
     * ArrayConverter arrayConverter = new ArrayConverter(String[].class, new StringConverter(), 2);
     * char[] allowedChars = { ':' };
     * arrayConverter.setAllowedChars(allowedChars);
     * ConvertUtils.register(arrayConverter, String[].class);
     * 
     * BeanUtil.populateAliasBean(dangaMemCachedConfig, readPropertiesToMap);
     * LOGGER.debug(JsonUtil.format(dangaMemCachedConfig));
     * 
* * 返回: * *
    {
        "maxConnection": 250,
        "expireTime": 180,
        "serverList":         [
            "172.20.31.23:11211",
            "172.20.31.22:11211"
        ],
        "weight": [2],
        "nagle": false,
        "initConnection": 10,
        "aliveCheck": false,
        "poolName": "sidsock2",
        "maintSleep": 30,
        "socketTo": 3000,
        "minConnection": 5
    }
     * 
* *
* * @param * the generic type * @param aliasBean * the bean * @param aliasAndValueMap * the key and value map * @return 如果 aliasAndValueMap 是null或者empty,返回 bean
* 如果 alias name 不在aliasAndValueMap中,或者值是emty,那么不会设置 aliasBean对象值 * @throws NullPointerException * 如果 bean 是null * @since 1.8.1 */ public static T populateAliasBean(T aliasBean,Map aliasAndValueMap){ Validate.notNull(aliasBean, "aliasBean can't be null!"); if (isNullOrEmpty(aliasAndValueMap)){ return aliasBean; } //--------------------------------------------------------------- Map propertyNameAndAliasMap = buildPropertyNameAndAliasMap(aliasBean.getClass()); for (Map.Entry entry : propertyNameAndAliasMap.entrySet()){ String alias = entry.getValue(); Object value = aliasAndValueMap.get(alias); if (isNotNullOrEmpty(value)){ setProperty(aliasBean, entry.getKey(), value); } } //--------------------------------------------------------------- return aliasBean; } //--------------------------------------------------------------- /** * 提取 klass {@link Alias} 注释,将 属性名字和 {@link Alias#name()} 组成map 返回. * * @param klass * the klass * @return 如果klass 没有 {@link Alias} 注释,返回 {@link Collections#emptyMap()} * @throws NullPointerException * 如果 klass 是null * @since 1.8.1 */ private static Map buildPropertyNameAndAliasMap(Class klass){ Validate.notNull(klass, "klass can't be null!"); //--------------------------------------------------------------- List aliasFieldsList = FieldUtils.getFieldsListWithAnnotation(klass, Alias.class); if (isNullOrEmpty(aliasFieldsList)){ return emptyMap(); } //--------------------------------------------------------------- //属性名字和key的对应关系 Map propertyNameAndAliasMap = newHashMap(aliasFieldsList.size()); for (Field field : aliasFieldsList){ Alias alias = field.getAnnotation(Alias.class); propertyNameAndAliasMap.put(field.getName(), alias.name()); } return propertyNameAndAliasMap; } // [end] //--------------------------------------------------------------- /** * 使用 valueMap 来构造一个 DynaBean. * *

说明:

*
*
    *
  1. 一般情况下,你可能不需要使用这个方法
  2. *
  3. 很适合那种属性值数量不确定,并且又不想在页面使用map来渲染的地方,比如制作多维度的图表
  4. *
  5. 程序内部,默认使用的是 {@link com.feilong.lib.beanutils.LazyDynaClass}
  6. *
  7. 不需要先创建一个期望的数据结构DynaClass,就能向{@link LazyDynaBean}中填充我们任意想填充的数据。
  8. *
  9. {@link LazyDynaBean}内部会根据我们填充进的数据(即使是一个map中的一个key-value pair),创建metadata的。
  10. *
*
* *

示例:

* *
* *
     * DynaBean newDynaBean = BeanUtil.newDynaBean(
     *                 toMap(//
     *                                 Pair.of("address", (Object) newHashMap()),
     *                                 Pair.of("firstName", (Object) "Fred"),
     *                                 Pair.of("lastName", (Object) "Flintstone")));
     * LOGGER.debug(JsonUtil.format(newDynaBean));
     * 
* * 返回: * *
     * {
     *  "address": {},
     *  "firstName": "Fred",
     *  "lastName": "Flintstone"
     * }
     * 
* *
* * @param valueMap * the value map * @return the dyna bean * @throws NullPointerException * 如果 valueMap 是null,或者 valueMap中有key是null * @throws IllegalArgumentException * 如果valueMap中有key是empty * @see com.feilong.lib.beanutils.LazyDynaBean * @since 1.8.1 * @since 1.13.2 param map change from {@code Map valueMap} to {@code Map valueMap} */ public static DynaBean newDynaBean(Map valueMap){ Validate.notNull(valueMap, "valueMap can't be null!"); //--------------------------------------------------------------- LazyDynaBean lazyDynaBean = new LazyDynaBean(); for (Map.Entry entry : valueMap.entrySet()){ String key = ConvertUtil.toString(entry.getKey()); Validate.notBlank(key, "entry.getKey() can't be blank!"); lazyDynaBean.set(key, entry.getValue()); } return lazyDynaBean; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy