public final class BeanUtil extends Object
BeanUtils
的再次封装.
- 目的是将原来的 checkedException 异常 转换成
BeanOperationException
PropertyUtils
与 BeanUtils
区别:BeanUtils.setProperty(pt1, "x", "9"); // 这里的9是String类型 PropertyUtils.setProperty(pt1, "x", 9); // 这里的是int类型 // 这两个类BeanUtils和PropertyUtils,前者能自动将int类型转化,后者不能
PropertyUtils
类和BeanUtils
类很多的方法在参数上都是相同的,但返回值不同.
BeanUtils着重于"Bean",返回值通常是String,
而PropertyUtils着重于属性,它的返回值通常是Object.
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<String,String>
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"));
copyProperty
和 setProperty
的区别:两者功能相似,区别点在于: copyProperty 不支持目标bean是索引类型,但是支持bean有索引类型的setter方法 copyProperty 不支持目标bean是Map类型,但是支持bean有Map类型的setter方法 如果我们只是为bean的属性赋值的话,使用BeanUtils.copyProperty(Object, String, Object)
就可以了; 而BeanUtils.setProperty(Object, String, Object)
方法是实现BeanUtils.populate(Object,Map)
机制的基础,也就是说如果我们需要自定义实现populate()方法,那么我们可以overrideBeanUtils.setProperty(Object, String, Object)
方法. 所以,做为一般的日常使用,BeanUtils.setProperty(Object, String, Object)
方法是不推荐使用的.
PropertyUtil
,
BeanInfo
,
PropertyDescriptor
,
MethodDescriptor
,
BeanUtils
,
Converter
,
DateConverter
,
DateTimeConverter
,
AbstractConverter
,
ConvertUtils.register(org.apache.commons.beanutils.Converter, Class)
,
ConvertUtilsBean.registerPrimitives(boolean)
,
ConvertUtilsBean.registerStandard(boolean, boolean)
,
ConvertUtilsBean.registerOther(boolean)
,
ConvertUtilsBean.registerArrays(boolean, int)
,
"org.springframework.beans.BeanUtils"Modifier and Type | Method and Description |
---|---|
static <T> T |
cloneBean(T bean)
|
static void |
copyProperties(Object toObj,
Object fromObj,
String... includePropertyNames)
将
fromObj 中的全部或者一组属性 includePropertyNames 的值,复制到 toObj 对象中. |
static DynaBean |
newDynaBean(Map<?,?> valueMap)
使用
valueMap 来构造一个 DynaBean. |
static <T> T |
populate(T bean,
Map<String,?> properties)
把properties/map里面的值
populate (填充)到bean 中. |
static <T> T |
populateAliasBean(T aliasBean,
Map<String,?> aliasAndValueMap)
将 alias 和value 的map
populate (填充)到 aliasBean . |
public static void copyProperties(Object toObj, Object fromObj, String... includePropertyNames)
fromObj
中的全部或者一组属性 includePropertyNames
的值,复制到 toObj
对象中.
如果需要copy的两个对象属性的类型一样,那么调用此方法会有性能消耗,强烈建议使用
PropertyUtil.copyProperties(Object, Object, String...)
- 这种copy都是 浅拷贝,复制后的2个Bean的同一个属性可能拥有同一个对象的ref,在使用时要小心,特别是对于属性为自定义类的情况 .
- 此方法调用了
BeanUtils.copyProperties(Object, Object)
,会自动进行Object--->String--->Object
类型转换- 如果指定了
includePropertyNames
,会调用getProperty(Object, String)
,在自动进行Object--->String
类型转换过程中,如果发现值是数组,只会取第一个元素重新构造数组转到toObj
中,规则参见ConvertUtil.toString(Object)
- 不支持
toObj
是map类型,从BeanUtilsBean.copyProperties(Object, Object)
源码可以看出,fromObj
可以是map
例如两个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");
Converter
:如果有
Date
类型的需要copy,那么需要先使用ConvertUtils.register(Converter, Class)
方法:
ConvertUtils.register(new DateLocaleConverter(Locale.US, DatePattern.TO_STRING_STYLE),Date.class);
BeanUtils.copyProperties(Object, Object)
与 PropertyUtils.copyProperties(Object, Object)
区别
BeanUtils.copyProperties(Object, Object)
提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,而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
BeanUtils.copyProperties(Object, Object)
的优点:
- 将 checkedException 异常转成了
BeanOperationException
RuntimeException,因为通常copy的时候出现了checkedException,也是普普通通记录下log,没有更好的处理方式- 支持 includePropertyNames 参数,允许针对性copy 个别属性
- 更多,更容易理解的的javadoc
toObj
- 目标对象fromObj
- 原始对象includePropertyNames
- 包含的属性名字数组,(can be nested/indexed/mapped/combo)BeanUtils.copyProperties(Object, Object)
,否则循环调用getProperty(Object, String)
再
setProperty(Object, String, Object)
到 toObj
对象中includePropertyNames
,含有 fromObj
没有的属性名字,将会抛出异常includePropertyNames
,含有 fromObj
有,但是 toObj
没有的属性名字,可以正常运行(跳过该属性设置),see
BeanUtilsBean.copyProperty(Object, String, Object)
Line391NullPointerException
- 如果 toObj
是null,或者 fromObj
是nullBeanOperationException
- 其他调用api有任何异常,转成BeanOperationException
返回BeanUtilsBean.copyProperties(Object, Object)
,
Bean复制的几种框架性能比较(Apache BeanUtils、PropertyUtils,Spring
BeanUtils,Cglib BeanCopier)public static <T> T cloneBean(T bean)
BeanUtils.cloneBean(Object)
.
- 这个方法通过默认构造函数建立一个bean的新实例,然后拷贝每一个属性到这个新的bean中,即使这个bean没有实现
Cloneable
接口 .- 是为那些本身没有实现clone方法的类准备的
- 在源码上看是调用了 getPropertyUtils().copyProperties(newBean, bean);最后实际上还是复制的引用,无法实现深clone
但还是可以帮助我们减少工作量的,假如类的属性不是基础类型的话(即自定义类),可以先clone出那个自定义类,在把他付给新的类,覆盖原来类的引用- 如果需要深度clone,可以使用
SerializationUtils.clone
,但是它的性能要慢很多倍- 由于内部实现是通过
Class.newInstance()
来构造新的对象,所以需要被clone的对象必须存在默认无参构造函数,否则会出现异常InstantiationException
- 目前无法clone list,总是返回empty list,参见 BeanUtils.cloneBean with List is broken
T
- the generic typebean
- Bean to be clonedNullPointerException
- 如果 bean
是nullBeanOperationException
- 在调用api有任何异常,转成BeanOperationException
返回BeanUtils.cloneBean(Object)
,
PropertyUtilsBean.copyProperties(Object, Object)
,
SerializationUtils.clone(java.io.Serializable)
,
ObjectUtils.clone(Object)
,
ObjectUtils.cloneIfPossible(Object)
,
BeanUtils.cloneBean with List is brokenpublic static <T> T populate(T bean, Map<String,?> properties)
populate
(填充)到bean
中.
- 将Map
<Key,value>
中的以值(String或String[])转换到目标bean对应的属性中,Key是目标bean的属性名.- apache的javadoc中,明确指明这个方法是为解析http请求参数特别定义和使用的,在正常使用中不推荐使用.推荐使用
copyProperties(Object, Object, String...)
方法- 底层方法原理
BeanUtilsBean.populate(Object, Map)
,循环map,调用BeanUtilsBean.setProperty(Object, String, Object)
方法 ,一一对应设置到bean
对象- 如果properties key中有bean中不存在的属性,那么该条数据自动忽略
- 如果properties key中有null,那么该条数据自动忽略,see
BeanUtilsBean.populate(Object, Map)
line 817bean
可以是Map类型,不过转换之后的key和value都会是Object类型,而不是声明的类型,seeBeanUtilsBean.setProperty(Object, String, Object)
line 928
User user = new User(); user.setId(5L); Map返回:<String, Object>
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 userName=feilong@feilong.cn // this is 胡编乱造的 account password=feilong // this is 胡编乱造的 account personal=feilong store tos=123456@163.com,1234567@163.cn ccs=123456test@163.com,123456test1@163.cn bccs=123456test2@163.com,123456test3@163.cn 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": "feilong@feilong.cn", "isDebug": true }此时你会发现,上面的 tos 期望值是 ["123456@163.com","1234567@163.cn"],但是和你的期望值不符合,ccs和bccs 也是相同的情况
因为,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": [ "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": "feilong@feilong.cn", "isDebug": true }如果你的配置文件的key和bean的属性不一致(比如大小写,有分隔符等情况),你可以使用
populateAliasBean(Object, Map)
T
- the generic typebean
- JavaBean whose properties are being populatedproperties
- Map keyed by property name,with the corresponding (String or String[]) value(s) to be setBeanUtilsBean.populate(Object, Map)
line 817NullPointerException
- 如果 bean
是null,或者如果 properties
是nullBeanOperationException
- 在调用BeanUtils.populate(Object, Map)
过程中有任何异常,转成BeanOperationException
返回BeanUtils.populate(Object, Map)
public static <T> T populateAliasBean(T aliasBean, Map<String,?> aliasAndValueMap)
populate
(填充)到 aliasBean
.
BeanUtil
有标准的populate功能:populate(Object, Map)
,但是要求 map的key 和 bean的属性名称必须是一一对应
有很多情况,比如 map 的key是 "memcached.alivecheck" 这样的字符串(常见于properties 的配置文件),或者是大写的 "ALIVECHECK" 的字符串(常见于第三方接口 xml属性名称)
而我们的bean里面的属性名称是标准的 java bean 规范的名字,比如 "aliveCheck",这时就没有办法直接使用populate(Object, Map)
方法了
你可以使用
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返回:<String, String>
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"],但是和你的期望值不符合,
因为,ArrayConverter
默认允许的字符 allowedChars 只有'.', '-'
,其他都会被做成分隔符你需要如此这般:
Map返回:<String, String>
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 }
T
- the generic typealiasBean
- the beanaliasAndValueMap
- the key and value mapaliasAndValueMap
是null或者empty,返回 bean
alias name
不在aliasAndValueMap
中,或者值是emty,那么不会设置 aliasBean
对象值NullPointerException
- 如果 bean
是nullpublic static DynaBean newDynaBean(Map<?,?> valueMap)
valueMap
来构造一个 DynaBean.
- 一般情况下,你可能不需要使用这个方法
- 很适合那种属性值数量不确定,并且又不想在页面使用map来渲染的地方,比如制作多维度的图表
- 程序内部,默认使用的是
LazyDynaClass
- 不需要先创建一个期望的数据结构DynaClass,就能向
LazyDynaBean
中填充我们任意想填充的数据。LazyDynaBean
内部会根据我们填充进的数据(即使是一个map中的一个key-value pair),创建metadata的。
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" }
valueMap
- the value mapNullPointerException
- 如果 valueMap
是null,或者 valueMap
中有key是nullIllegalArgumentException
- 如果valueMap
中有key是emptyMap<String, ?> valueMap
to Map<?, ?> valueMap
LazyDynaBean
Copyright © 2008-2019 by feilong