java 对象复制工具_五种对象属性拷贝工具类大全 Bean的属性拷贝从此不用愁

1.引言

大家在做java开发时,肯定会遇到api层参数对象传递给服务层,

或者把service层的对象传递给dao层,他们之间又不是同一个类型对象,

但字段又是一样,如果还是用普通的get、set方式来处理话,比较繁琐,.... 那么来跟我学.....

"天下武功,唯快不破"....突突突--biubiu---

2.大全套餐

equation?tex=%5Ccolor%7B%234285f4%7D%7B1.getter%2Fsetter%7D

使用get/set不用说了,字段一多脑壳大....

equation?tex=%5Ccolor%7B%234285f4%7D%7B2.BeanCopier%7D

使用了cglib的修改字节码,真的是动态Read Writer getter/setter 快的一逼啊 源码过于简单 直接上代码 我使用的最基本的,大家可以自定义Converter,定以后完全按照Converter转换

需要注意的是:

1.当源类和目标类的属性名称、类型都相同,拷贝结果棒棒哒。

2.当源对象和目标对象的属性名称相同、类型不同,那么名称相同而类型不同的属性不会被拷贝。另外注意,原始类型(int,short,char)和 他们的包装类型,在这里都被当成了不同类型。因此不会被拷贝。

3.源类或目标类的setter比getter少,拷贝没问题,此时setter多余,但是不会报错。

4.源类和目标类有相同的属性(两者的getter都存在),但是目标类的setter不存在, 此时会抛出NullPointerException(这个在高版本bug已经修改测试通过,我使用的49.0)

上code

import lombok.extern.slf4j.Slf4j;

import org.springframework.cglib.beans.BeanCopier;

import java.util.Map;

import java.util.concurrent.ConcurrentHashMap;

/**

* @author LiJing

* @ClassName: BeanCopyUtils

* @Description: 基于BeanCopier的属性拷贝

* @date 2019/4/17 9:15

* 凡是和反射相关的操作,基本都是低性能的。凡是和字节码相关的操作,基本都是高性能的。

*

*/

@Slf4j

public class BeanCopyUtils {

/**

* 创建过的BeanCopier实例放到缓存中,下次可以直接获取,提升性能

*/

private static final Map BEAN_COPIERS = new ConcurrentHashMap<>();

/**

* 该方法没有自定义Converter,简单进行常规属性拷贝

*

* @param srcObj 源对象

* @param destObj 目标对象

*/

public static void copy(Object srcObj, Object destObj) {

String key = genKey(srcObj.getClass(), destObj.getClass());

BeanCopier copier;

if (!BEAN_COPIERS.containsKey(key)) {

copier = BeanCopier.create(srcObj.getClass(), destObj.getClass(), false);

BEAN_COPIERS.put(key, copier);

} else {

copier = BEAN_COPIERS.get(key);

}

copier.copy(srcObj, destObj, null);

}

private static String genKey(Class> srcClazz, Class> destClazz) {

return srcClazz.getName() + destClazz.getName();

}

}

复制代码

equation?tex=%5Ccolor%7B%234285f4%7D%7B3.spring.BeanUtils%7D

这个org.springframework.beans.BeanUtils,这个比较骚,使用反射,反射就比较慢了,要加载字节码,反编译,再实例化,再映射属性.....来来上二斤代码,先吃着 有用的地方不多大家看着用哈,copyProperties其实有这个方法就够了,

这个是把源码改造后的方法:

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeanUtils;

import org.springframework.beans.BeansException;

import org.springframework.beans.FatalBeanException;

import org.springframework.util.Assert;

import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

import java.lang.reflect.Modifier;

import java.math.BigDecimal;

import java.sql.Timestamp;

import java.util.Arrays;

import java.util.Date;

import java.util.List;

/**

* @author LiJing

* @ClassName: MyBeanUtils

* @Description: 属性拷贝工具类

* @date 2018/4/10 11:54

*/

@Slf4j

public class MyBeanUtils extends BeanUtils {

/**

* 从org.springframework.beans.BeanUtils类中直接复制过来

*

* @param source

* @param target

* @throws BeansException

*/

public static void copyProperties(Object source, Object target) throws BeansException {

copyProperties(source, target, null, (String[]) null);

}

/**

* 从org.springframework.beans.BeanUtils类中直接复制过来,修改部分代码,不为null才复制

*

* @param source

* @param target

* @param editable

* @param ignoreProperties

* @throws BeansException

*/

private static void copyProperties(Object source, Object target, Class> editable, String... ignoreProperties)

throws BeansException {

Assert.notNull(source, "Source must not be null");

Assert.notNull(target, "Target must not be null");

Class> actualEditable = target.getClass();

if (editable != null) {

if (!editable.isInstance(target)) {

throw new IllegalArgumentException("Target class [" + target.getClass().getName() +

"] not assignable to Editable class [" + editable.getName() + "]");

}

actualEditable = editable;

}

PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);

List ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;

for (PropertyDescriptor targetPd : targetPds) {

Method writeMethod = targetPd.getWriteMethod();

if (writeMethod != null && (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) {

PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());

if (sourcePd != null) {

Method readMethod = sourcePd.getReadMethod();

if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {

try {

if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {

readMethod.setAccessible(true);

}

Object value = readMethod.invoke(source);

// 判断被复制的属性是否为null, 如果不为null才复制

if (value != null) {

if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {

writeMethod.setAccessible(true);

}

writeMethod.invoke(target, value);

}

} catch (Throwable ex) {

throw new FatalBeanException(

"不能拷贝属性 '" + targetPd.getName() + "' 从原对象给目标对象,详细原因见:", ex);

}

}

}

}

}

}

/**

* 构造函数.

*/

public MyBeanUtils () {

throw new RuntimeException("this is a util class,can not instance");

}

复制代码

equation?tex=%5Ccolor%7B%234285f4%7D%7B4.apache.BeanUtils%7D

还有很多属性拷贝的方法例如org.apache.commons.beanutils.BeanUtils的copyProperties(Object dest, Object src)这里不举例了 类似于Spring的属性拷贝

equation?tex=%5Ccolor%7B%234285f4%7D%7B5.baidu.easymapper%7D

还有使用easymapper来进行对象映射,但在项目中存在不稳性,偶尔出现映射不上的问题,会报异常com.baidu.unbiz.easymapper.exception.MappingException,大家可自行研究

bean转map可以:

/**

* 将对象装换为map

* @param bean

* @return

*/

public static Map beanToMap(T bean) {

Map map = Maps.newHashMap();

if (bean != null) {

BeanMap beanMap = BeanMap.create(bean);

for (Object key : beanMap.keySet()) {

map.put(key+"", beanMap.get(key));

}

}

return map;

}

复制代码

3.小结

其实有很多种方法进行属性拷贝的,例如dozer等等 下面看下测试性能吧:以:万级进行测试,我觉得Cglib太给力了.可以在遇到属性拷贝瓶颈时考虑.当然他们各有优点哈,功能也不尽相同.还需要多使用体会.

输出结果:手动Copy > cglibCopy > springBeanUtils > apachePropertyUtils > apacheBeanUtils 可以理解为: 手工复制 > cglib > 反射 > Dozer。

类型Framework

测试性能(10000调用次)time

Pure get/set

10ms

Cglib Beancopier

14ms

Easy mapper

46ms

Spring BeanUtils

96ms

Apache BeanUtils

249ms

Apache PropertieyUtils

130ms

Dozer

770ms

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值