您的位置:首页 > 运维架构 > Apache

Spring BeanUtils.copyProperties和apache commons-beanutils

2016-09-23 17:24 369 查看
from:http://blog.csdn.net/Victor_Cindy1/article/details/52450278

业务系统中经常需要两个对象进行属性的拷贝,不能否认逐个的对象拷贝是最快速最安全的做法,但是当数据对象的属性字段数量超过程序员的容忍的程度,代码因此变得臃肿不堪,使用一些方便的对象拷贝工具类将是很好的选择。

性能对比: BeanCopier > PropertyUtils > BeanUtils. 其中BeanCopier的性能高出另外两个100数量级。

目前流行的较为公用认可的工具类:

Apache的两个版本:(反射机制)

org.apache.commons.beanutils.PropertyUtils.copyProperties(Object dest, Object orig)

org.apache.commons.beanutils.BeanUtils.copyProperties(Object dest, Object orig)

Spring版本:(反射机制)

org.springframework.beans.BeanUtils.copyProperties(Object source, Object target, Class editable, String[] ignoreProperties)

cglib版本:(使用动态代理,效率高)

net.sf.cglib.beans.BeanCopier.copy(Object paramObject1, Object paramObject2, Converter paramConverter)

缺陷预防:

@Document(collection = "project_info")
public class ProjectInfo extends BaseEntity {
@TextIndexed(weight = 3)
private String name;
@TextIndexed(weight = 2)
private String description;
@Field("nick_name")
private String nickName;
@Field("is_available")
private boolean isAvailable = true;
@Field("is_public")
private boolean isPublic;
@Field("create_at")
private Date createAt;
@Field("last_activity_at")
private Date lastActivityAt;
@Field("web_url")
private String webUrl;
@Field("repo_url")
private String repoUrl;
@Field("avatar_url")
private String avatarUrl;
private User owner;
@Indexed
private long stars;
private Map<String, Collection<String>> categories;
private Collection<ProjectMember> members;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean getIsAvailable() {
return isAvailable;
}
public void setIsAvailable(boolean available) {
isAvailable = available;
}
public boolean getIsPublic() {
return isPublic;
}
public void setIsPublic(boolean aPublic) {
isPublic = aPublic;
}
public Date getCreateAt() {
return createAt;
}
public void setCreateAt(Date createAt) {
this.createAt = createAt;
}
public Date getLastActivityAt() {
return lastActivityAt;
}
public void setLastActivityAt(Date lastActivityAt) {
this.lastActivityAt = lastActivityAt;
}
public String getWebUrl() {
return webUrl;
}
public void setWebUrl(String webUrl) {
this.webUrl = webUrl;
}
public String getRepoUrl() {
return repoUrl;
}
public void setRepoUrl(String repoUrl) {
this.repoUrl = repoUrl;
}
public String getAvatarUrl() {
return avatarUrl;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
public Map<String, Collection<String>> getCategories() {
return categories;
}
public void setCategories(Map<String, Collection<String>> categories) {
this.categories = categories;
}
public User getOwner() {
return owner;
}
public void setOwner(User owner) {
this.owner = owner;
}
public long getStars() {
return stars;
}
public void setStars(long stars) {
this.stars = stars;
}
public Collection<ProjectMember> getMembers() {
return members;
}
public void setMembers(Collection<ProjectMember> members) {
this.members = members;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
}


**
* Created by ${denghb} on 2016/8/9.
*/
public class ProjectInfoDto {
private String id;
private String name;
private String nickName;
private String description;
private String avatarUrl;
private Date createTime;
private Date updateTime;
private String status;
private boolean isAvailable;
private boolean isPublic;
private UserDto creator;
private Collection<ProjectMemberDto> members;
private Collection<ProjectReleasedVersionDto> releasedVersions;
private Map<String, Collection<String>> categories;
public Map<String, Collection<String>> getCategories() {
return categories;
}
public void setCategories(Map<String, Collection<String>> categories) {
this.categories = categories;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getAvatarUrl() {
return avatarUrl;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public boolean getIsAvailable() {
return isAvailable;
}
public void setIsAvailable(boolean available) {
isAvailable = available;
}
public boolean getIsPublic() {
return isPublic;
}
public void SetIsPublic(boolean aPublic) {
isPublic = aPublic;
}
public UserDto getCreator() {
return creator;
}
public void setCreator(UserDto creator) {
this.creator = creator;
}
public Collection<ProjectMemberDto> getMembers() {
return members;
}
public void setMembers(Collection<ProjectMemberDto> members) {
this.members = members;
}
public Collection<ProjectReleasedVersionDto> getReleasedVersions() {
return releasedVersions;
}
public void setReleasedVersions(Collection<ProjectReleasedVersionDto> releasedVersions) {
this.releasedVersions = releasedVersions;
}
}


private ProjectInfo trans2ProjectInfo(ProjectInfoDto infoDto) {
ProjectInfo projectInfo = getProjectInfo(infoDto);
if ("DELETE".equalsIgnoreCase(infoDto.getStatus())) {
projectInfo.setDeleteFlag(true);
return projectInfo;
}
// 复制简单属性到ProjectInfo中
BeanUtils.copyProperties(infoDto, projectInfo, "members", "categories");
projectInfo.setCreateAt(infoDto.getCreateTime());
projectInfo.setLastActivityAt(infoDto.getUpdateTime());
projectInfo.setCategories(getCategories(infoDto, projectInfo));
// 添加owner属性
User owner = getOwner(infoDto);
projectInfo.setOwner(owner);
// 添加members属性
Collection<ProjectMember> members = getProjectMembers(infoDto);
projectInfo.setMembers(members);
return projectInfo;
}


注意1:Spring 的BeanUtils.copyProperties在拷贝属性时忽略空值解决办法。

当src对象的键值为Null时,就会把target对象的对应键值覆盖成空了。

package com.dhb.springmvc.util.beanUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import java.util.HashSet;
import java.util.Set;
/**
* 当src对象的键值为Null时,使用如下方式就不会把target对象的对应键值覆盖成空了
* Created by ${denghb} on 2016/9/6.
*/
public class BeanUtilIgnore {
public static String[] getNullPropertyNames ( Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<>();
for(java.beans.PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) emptyNames.add(pd.getName());
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}
public static void copyPropertiesIgnoreNull( Object src, Object target){
BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}
}:


然后在调用这个方法进行复制:

BeanUtils.copyProperties(examLifeStyle, examDetail, getNullPropertyNames(examLifeStyle));

注意2:beanutils.Converter使用案例,增强apache的beanUtils的拷贝属性,注册一些新的类型转换。

package com.dhb.springmvc.util.beanUtil;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import java.lang.reflect.InvocationTargetException;
/**
* Created by ${denghb} on 2016/9/6.
*/
public class BeanUtilsEx extends BeanUtils {
public static void copyProperties(Object dest, Object orig) {
try {
BeanUtils.copyProperties(dest, orig);
} catch (IllegalAccessException ex) {
ex.printStackTrace();
} catch (InvocationTargetException ex) {
ex.printStackTrace();
}
}
static {
ConvertUtils.register(new DateConverter(null), java.util.Date.class);
//ConvertUtils.register(new BigDecimalConvert(), BigDecimal.class);
}
}

package com.dhb.springmvc.util.beanUtil;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.lang.StringUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
/**
* 简易DateConverter.
* 供Apache BeanUtils 做转换,默认时间格式为yyyy-MM-dd,可由构造函数改变.
* Created by ${denghb} on 2016/9/6.
*/
public class DateConverter implements Converter {
private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
public DateConverter(String formatPattern) {
if (StringUtils.isNotBlank(formatPattern)) {
format = new SimpleDateFormat(formatPattern);
}
}
@Override
public Object convert(Class aClass, Object value) {
String dateStr = (String) value;
if (StringUtils.isNotBlank(dateStr)) {
try {
return format.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
}


注意3:BeanUtils拷贝null属性的问题

问题现象

       1.当源对象(a)中存在一个java.sql.Date类型的属性并且值为null,目标对象(b)中也存在这个同名同类型的属性。把a对象属性值copy给b时BeanUtils.copyProperties(b, a);会抛出异常;

       2.当源对象(a)中存在一个java.sql.Date类型的属性并且值为null,目标对象(b)不存在这个同名同类型的属性,copy时没问题。

解决方法

       从convert方法可以看出只要想办法把useDefault和defaultValue设值就能解决,SqlDateConverter的另外一个构造方法可以设置这两个值,问题是从哪里把这个自己构造的converter 注册进去。从BeanUtils到BeanUtilsBean再到ConvertUtilsBean的找,发现都是写死的调用ConvertUtilsBean.deregister()方法注册的,最后发现ConvertUtilsBean有个register(Converter converter,
Class clazz)方法可以注册,而ConvertUtilsBean又是在BeanUtilsBean里new出来的,那么只要获取到这个ConvertUtilsBean即可,BeanUtilsBean提供了获取的方法。因此在BeanUtils.copyProperties(b, a);加上下面这句代码即可

BeanUtilsBean.getInstance().getConvertUtils().register(new SqlDateConverter(null),Date.class);

另外有一个辅助类也可以,

ConvertUtils.register(new SqlDateConverter(null),Date.class);

上图一张,你们就明白了。



关于bean复制,如果属性较少,建议直接写个方法完成get/set即可。如果属性较多,可以自己采用反射实现一个满足自己需要的工具类,或者使用spring的那个beanutils类,不建议使用commons-beanutils包中的那个BeanUtils类,刚看了下,这个类对于内部静态类的对象复制也会出现问题,检验太复杂了,常会出现一些诡异的问题。毕竟我们bean复制一般就是简单的属性copy而已。

而且,由于这些BeanUtils类都是采用反射机制实现的,对程序的效率也会有影响。因此,慎用BeanUtils.copyProperties!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: