大家在做java开发时,肯定会遇到api层参数对象传递给服务层,
或者把service层的对象传递给dao层,他们之间又不是同一个类型对象,
但字段又是一样,如果还是用普通的get、set方式来处理话,比较繁琐,.... 那么你来跟我学....."天下武功,唯快不破"===>要的就是快....
1.使用get/set不用说了,字段一多脑壳大....
2.BeanCopier 使用了cglib的修改字节码,真的是动态Read Writer getter/setter 快的一逼啊 源码过于简单 直接上代码 我使用的最基本的,大家可以自定义Converter,定以后完全按照Converter转换
需要注意的是:
* 1.当源类和目标类的属性名称、类型都相同,拷贝结果棒棒哒。
* 2.当源对象和目标对象的属性名称相同、类型不同,那么名称相同而类型不同的属性不会被拷贝。另外注意,原始类型(int,short,char)和 他们的包装类型,在这里都被当成了不同类型。因此不会被拷贝。
* 3.源类或目标类的setter比getter少,拷贝没问题,此时setter多余,但是不会报错。
* 4.源类和目标类有相同的属性(两者的getter都存在),但是目标类的setter不存在, 此时会抛出NullPointerException(这个在高版本bug已经修改测试通过,我使用的49.0)
1 /** 2 * @author LiJing 3 * @ClassName: BeanCopyUtils 4 * @Description: 基于BeanCopier的属性拷贝 5 * @date 2019/4/17 9:1521 * * 凡是和反射相关的操作,基本都是低性能的。凡是和字节码相关的操作,基本都是高性能的。 22 * <p/> 23 */ 24 @Slf4j 25 public class BeanCopyUtils { 26 27 /** 28 * 创建过的BeanCopier实例放到缓存中,下次可以直接获取,提升性能 29 */ 30 private static final Map<String, BeanCopier> BEAN_COPIERS = new ConcurrentHashMap<>(); 31 32 /** 33 * 该方法没有自定义Converter,简单进行常规属性拷贝 34 * 35 * @param srcObj 源对象 36 * @param destObj 目标对象 37 */ 38 public static void copy(final @NotNull Object srcObj, final @NotNull Object destObj) { 39 String key = genKey(srcObj.getClass(), destObj.getClass()); 40 BeanCopier copier; 41 if (!BEAN_COPIERS.containsKey(key)) { 42 copier = BeanCopier.create(srcObj.getClass(), destObj.getClass(), false); 43 BEAN_COPIERS.put(key, copier); 44 } else { 45 copier = BEAN_COPIERS.get(key); 46 } 47 copier.copy(srcObj, destObj, null); 48 } 49 50 private static String genKey(Class<?> srcClazz, Class<?> destClazz) { 51 return srcClazz.getName() + destClazz.getName(); 52 } 53 }
3.接下来再来玩一个org.springframework.beans.BeanUtils,这个比较骚,使用反射,反射就比较慢了,要加载字节码,反编译,再实例化,再映射属性.....来来上二斤代码,先吃着 有用的地方不多大家看着用哈<copyProperties>其实有这个方法就够了
1 import lombok.extern.slf4j.Slf4j; 2 import org.springframework.beans.BeanUtils; 3 import org.springframework.beans.BeansException; 4 import org.springframework.beans.FatalBeanException; 5 import org.springframework.util.Assert; 6 import org.springframework.util.ClassUtils; 7 17 18 /** 19 * @author LiJing 20 * @ClassName: BeanUtils 21 * @Description: 属性拷贝工具类 22 * @date 2018/4/10 11:54 23 */ 24 @Slf4j 25 public class MyBeanUtils extends BeanUtils { 26 27 /** 28 * 从org.springframework.beans.BeanUtils类中直接复制过来 29 * 30 * @param source 31 * @param target 32 * @throws BeansException 33 */ 34 public static void copyProperties(Object source, Object target) throws BeansException { 35 copyProperties(source, target, null, (String[]) null); 36 } 37 38 /** 39 * 从org.springframework.beans.BeanUtils类中直接复制过来,修改部分代码,不为null才复制 40 * 41 * @param source 42 * @param target 43 * @param editable 44 * @param ignoreProperties 45 * @throws BeansException 46 */ 47 private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties) 48 throws BeansException { 49 50 Assert.notNull(source, "Source must not be null"); 51 Assert.notNull(target, "Target must not be null"); 52 53 Class<?> actualEditable = target.getClass(); 54 if (editable != null) { 55 if (!editable.isInstance(target)) { 56 throw new IllegalArgumentException("Target class [" + target.getClass().getName() + 57 "] not assignable to Editable class [" + editable.getName() + "]"); 58 } 59 actualEditable = editable; 60 } 61 PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); 62 List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null; 63 64 for (PropertyDescriptor targetPd : targetPds) { 65 Method writeMethod = targetPd.getWriteMethod(); 66 if (writeMethod != null && (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) { 67 PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); 68 if (sourcePd != null) { 69 Method readMethod = sourcePd.getReadMethod(); 70 if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { 71 try { 72 if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { 73 readMethod.setAccessible(true); 74 } 75 Object value = readMethod.invoke(source); 76 // 判断被复制的属性是否为null, 如果不为null才复制 77 if (value != null) { 78 if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { 79 writeMethod.setAccessible(true); 80 } 81 writeMethod.invoke(target, value); 82 } 83 } catch (Throwable ex) { 84 throw new FatalBeanException( 85 "不能拷贝属性 '" + targetPd.getName() + "' 从原对象给目标对象,详细原因见:", ex); 86 } 87 } 88 } 89 } 90 } 91 } 92 93 94 /** 95 * 构造函数. 96 */ 97 public MyBeanUtils() { 98 throw new RuntimeException("this is a util class,can not instance"); 99 } 411 }
4.还有很多属性拷贝的方法例如org.apache.commons.beanutils.BeanUtils的copyProperties(Object dest, Object src)这里不举例了 类似于Spring的属性拷贝
5.还有使用easymapper来进行对象映射,但在项目中存在不稳性,偶尔出现映射不上的问题,会报异常com.baidu.unbiz.easymapper.exception.MappingException,大家可自行研究
其实有很多种方法进行属性拷贝的,例如dozer等等 下面看下测试性能吧:以:万级进行测试,我觉得Cglib太给力了.可以在遇到属性拷贝瓶颈时考虑.当然他们各有优点哈,功能也不尽相同.还需要多使用体会.
输出结果:手动Copy > cglibCopy > springBeanUtils > apachePropertyUtils > apacheBeanUtils 可以理解为: 手工复制 > cglib > 反射 > Dozer。
类型Framework | 测试性能(10000调用次)time |
Pure get/set | 10ms |
Easy mapper | 46ms |
Cglib Beancopier | 11ms |
Spring BeanUtils | 94ms |
Apache BeanUtils | 249ms |
Apache PropertieyUtils | 130ms |
Dozer | 770ms |