大家在做java开发时,肯定会遇到api层参数对象传递给服务层, 或者把service层的对象传递给dao层,他们之间又不是同一个类型对象, 但字段又是一样,如果还是用普通的get、set方式来处理话,比较繁琐,.... 那么来跟我学.....
"天下武功,唯快不破"....突突突--biubiu---
使用get/set不用说了,字段一多脑壳大....
使用了cglib的修改字节码,真的是动态Read Writer getter/setter 快的一逼啊 源码过于简单 直接上代码 我使用的最基本的,大家可以自定义Converter,定以后完全按照Converter转换 需要注意的是:
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 * 凡是和反射相关的操作,基本都是低性能的。凡是和字节码相关的操作,基本都是高性能的。 * <p/> */ @Slf4j public class BeanCopyUtils { /** * 创建过的BeanCopier实例放到缓存中,下次可以直接获取,提升性能 */ private static final Map<String, BeanCopier> 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(); } } 复制代码
这个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<String> 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"); } 复制代码
还有很多属性拷贝的方法例如org.apache.commons.beanutils.BeanUtils的copyProperties(Object dest, Object src)这里不举例了 类似于Spring的属性拷贝
还有使用easymapper来进行对象映射,但在项目中存在不稳性,偶尔出现映射不上的问题,会报异常com.baidu.unbiz.easymapper.exception.MappingException,大家可自行研究
其实有很多种方法进行属性拷贝的,例如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 |