• BeanUtils.copyProperties(复制对象属性方法)


    原创:https://liaoyongguang.blog.csdn.net/

    背景:

    在电商项目中所需要的业务非常多,所以我们的业务数据也会有很多种,这个时候就会有什么VO,DTO,PO等等这些,把业务和我们的基础数据进行分离转换。但是一直都没有什么好一点的转换类。后来用了一下BeanUtils.copyPropertie,和PropertyUtils.copyProperties()的方法,发现其效率非常低。这里也简单总结了一下他们的用法及原理以及自己实现的转换类;

    用法:

    BeanUtils.copyProperties("转换后的类", "要转换的类");
     PropertyUtils.copyProperties("转换后的类", "要转换的类");

    List<MsgUserRecord> idLists = userRecordMapper.getTitleId(Id);
    if(CollectionUtils.isEmpty(idLists))return null;
    for(MsgUserRecord userRecord : idLists) {
    MsgUserRecordResp userRecordResp = new MsgUserRecordResp();
    BeanUtils.copyProperties(userRecord, userRecordResp);
     

    用法其实很简单,第一个参数是转换后的类,第二个参数是待转换的类;我们可以理解成为后转前;

    原理:

    其原理是通过JDK自带的反射机制动态的去get,set,从而去转换我们的类。但是要注意一点他们所支持的数据类型,还有一个就是假如一个类里面又写了一个类,例如这种:

    1. public class Name{
    2.  
    3. }
    4. class Name1{
    5.  
    6. }

    一般叫做内部类,像这种类进行转换的时候,是不会成功的。因为在里面进行读写校验的时候不会通过;

    1. PropertyDescriptor[] origDescriptors =
    2. getPropertyDescriptors(orig);
    3. for (int i = 0; i < origDescriptors.length; i++) {
    4. String name = origDescriptors[i].getName();
    5. if (isReadable(orig, name) && isWriteable(dest, name)) {
    6. try {
    7. Object value = getSimpleProperty(orig, name);
    8. if (dest instanceof DynaBean) {
    9. ((DynaBean) dest).set(name, value);
    10. } else {
    11. setSimpleProperty(dest, name, value);
    12. }
    13. } catch (NoSuchMethodException e) {
    14. if (log.isDebugEnabled()) {
    15. log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
    16. }
    17. }
    18. }
    19. }

    上图是JDK的源代码,我们执行isRead和isWrite的时候并不会通过;

    注意差异,

    主要的区别在于BeanUtils
    提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,而前者不支持这个功能,但是速度会更快一些。BeanUtils支持的转换类型如下:
    1. * java.lang.BigDecimal
    2. * java.lang.BigInteger
    3. * boolean and java.lang.Boolean
    4. * byte and java.lang.Byte
    5. * char and java.lang.Character
    6. * java.lang.Class
    7. * double and java.lang.Double
    8. * float and java.lang.Float
    9. * int and java.lang.Integer
    10. * long and java.lang.Long
    11. * short and java.lang.Short
    12. * java.lang.String
    13. * java.sql.Date
    14. * java.sql.Time
    15. * java.sql.Timestamp


    阅读其源码,发现其内部是使用了装饰者模式,我发现Java得工具类很喜欢使用这种模式,而且也十分好用;

    他们都使用到了BeanutilsBean和PropertyUtilsBean只不过BeanUtils多了一个转换的功能而已,但是性能上要比

    PropertyUtils慢一些,其实两个都很慢,最好不要使用;

    重写反射转换:

    1. /**
    2. * @param obj 转换的对象值
    3. * @param clz 类对象
    4. * @return 转换后的对象
    5. */
    6. public static<T> T transferObject(Object obj,Class clz){
    7. T result = null;
    8. if(obj!=null&&!obj.equals("")){
    9. Method[] methods = obj.getClass().getMethods();
    10. try {
    11. result = (T)clz.newInstance();
    12. } catch (Exception e1) {
    13. return null;
    14. }
    15. Method m;
    16. for(int i=0;i<methods.length;i++){
    17. m = methods[i];
    18. try {
    19. if(m.getName().startsWith("set")){
    20. String fieldName = m.getName().replaceFirst("set", "");
    21. Method method = result.getClass().getMethod(m.getName(), m.getParameterTypes());
    22. Method getMethod = obj.getClass().getMethod("get"+fieldName, new Class[]{});
    23. method.invoke(result, getMethod.invoke(obj, new Object[]{}));
    24. }
    25. } catch (Exception e) {
    26. continue;
    27. }
    28. }
    29. }
    30. return result;
    31. }

    上面这个方法也是用了Java反射去写的,但是少了很多校验以及转换。所以在100万条数据的时候,效率是3739毫秒,而使用BeanUtils是5000毫秒左右。两个效率都不高;

    ReflectASM,高性能的反射:

    什么是ReflectASM    ReflectASM是一个很小的java类库,主要是通过asm生产类来实现java反射,执行速度非常快,看了网上很多和反射的对比,觉得ReflectASM比较神奇,很想知道其原理,下面介绍下如何使用及原理;

    1. public static void main(String[] args) {
    2. User user = new User();
    3. //使用reflectasm生产User访问类
    4. MethodAccess access = MethodAccess.get(User.class);
    5. //invoke setName方法name值
    6. access.invoke(user, "setName", "张三");
    7. //invoke getName方法 获得值
    8. String name = (String)access.invoke(user, "getName", null);
    9. System.out.println(name);
    10. }

    原理 
       上面代码的确实现反射的功能,代码主要的核心是 MethodAccess.get(User.class); 
    看了下源码,这段代码主要是通过asm生产一个User的处理类 UserMethodAccess(这个类主要是实现了invoke方法)的ByteCode,然后获得该对象,通过上面的invoke操作user类。 

    ASM反射转换:

    1. private static Map<Class, MethodAccess> methodMap = new HashMap<Class, MethodAccess>();
    2.  
    3. private static Map<String, Integer> methodIndexMap = new HashMap<String, Integer>();
    4.  
    5. private static Map<Class, List<String>> fieldMap = new HashMap<Class, List<String>>();
    6.  
    7. public static void copyProperties(Object desc, Object orgi) {
    8. MethodAccess descMethodAccess = methodMap.get(desc.getClass());
    9. if (descMethodAccess == null) {
    10. descMethodAccess = cache(desc);
    11. }
    12. MethodAccess orgiMethodAccess = methodMap.get(orgi.getClass());
    13. if (orgiMethodAccess == null) {
    14. orgiMethodAccess = cache(orgi);
    15. }
    16.  
    17. List<String> fieldList = fieldMap.get(orgi.getClass());
    18. for (String field : fieldList) {
    19. String getKey = orgi.getClass().getName() + "." + "get" + field;
    20. String setkey = desc.getClass().getName() + "." + "set" + field;
    21. Integer setIndex = methodIndexMap.get(setkey);
    22. if (setIndex != null) {
    23. int getIndex = methodIndexMap.get(getKey);
    24. // 参数一需要反射的对象
    25. // 参数二class.getDeclaredMethods 对应方法的index
    26. // 参数对三象集合
    27. descMethodAccess.invoke(desc, setIndex.intValue(),
    28. orgiMethodAccess.invoke(orgi, getIndex));
    29. }
    30. }
    31. }
    32.  
    33. // 单例模式
    34. private static MethodAccess cache(Object orgi) {
    35. synchronized (orgi.getClass()) {
    36. MethodAccess methodAccess = MethodAccess.get(orgi.getClass());
    37. Field[] fields = orgi.getClass().getDeclaredFields();
    38. List<String> fieldList = new ArrayList<String>(fields.length);
    39. for (Field field : fields) {
    40. if (Modifier.isPrivate(field.getModifiers())
    41. && !Modifier.isStatic(field.getModifiers())) { // 是否是私有的,是否是静态的
    42. // 非公共私有变量
    43. String fieldName = StringUtils.capitalize(field.getName()); // 获取属性名称
    44. int getIndex = methodAccess.getIndex("get" + fieldName); // 获取get方法的下标
    45. int setIndex = methodAccess.getIndex("set" + fieldName); // 获取set方法的下标
    46. methodIndexMap.put(orgi.getClass().getName() + "." + "get"
    47. + fieldName, getIndex); // 将类名get方法名,方法下标注册到map中
    48. methodIndexMap.put(orgi.getClass().getName() + "." + "set"
    49. + fieldName, setIndex); // 将类名set方法名,方法下标注册到map中
    50. fieldList.add(fieldName); // 将属性名称放入集合里
    51. }
    52. }
    53. fieldMap.put(orgi.getClass(), fieldList); // 将类名,属性名称注册到map中
    54. methodMap.put(orgi.getClass(), methodAccess);
    55. return methodAccess;
    56. }
    57. }

    执行1000000条效率80几毫秒,效率已经没问题了;

  • 相关阅读:
    数据结构复习代码——递归实现二叉树的定义以及创建
    数据结构复习代码——递归实现广义表的创建以及遍历
    数据结构复习代码——矩阵的相关操作以及矩阵快速转置算法的实现以及遇到问题及解决
    数据结构复习代码——基于顺序存储下串的相关操作以及串匹配模式算法的实现
    Redis集群报错:(error) CROSSSLOT Keys in request don't hash to the same slot 的解决办法
    网址信息
    ERROR 1100 (HY000): Table '表名' was not locked with LOCK TABLES (表被锁定)
    java中字符串Base64、16进制的转解码函数DatatypeConverter.printBase64Binary、parseBase64Binary用法
    telnetserver安装
    基于LINUX的MySql二进制本地升级实施文档
  • 原文地址:https://www.cnblogs.com/yangsanluo/p/14274121.html
Copyright © 2020-2023  润新知