zoukankan      html  css  js  c++  java
  • 对象Bean与Map互转问题

    一、摘要

    在实际开发过程中,经常碰到需要进行对象与map之间互转的问题,其实对于对象、Map 之间进行互转有很多种方式,下面我们一起来梳理一下:

    • 利用 JSON 工具包,将对象转成字符串,之后再转成 Map,这种需要转换2次,相对来说效率比较底;
    • 利用 Java 反射,获取 Bean 类的属性和值,再转换到 Map 对应的键值对中,相对来说这种方法效率高些,在实现上比较麻烦;
    • 利用 Java 的内省(Introspector) 实现,获取 Bean 类的属性和值,Map与对象互转,效率比较高;
    • 利用 apache 中的 BeanUtils工具包进行操作,底层实现类似方法三;
    • 利用net.sf.cglib.beans.BeanMap类中的方法,这种方式效率也非常高;

    二、常用方法

    也不多说了,直接show code,为了更接近实际场景,我们新建两个实体类UserRole,假设一个用户有多个角色,如下:

    publicclass User {
    
        private String userId;
    
        private String userName;
    
        private List<Role> roleList;
    
        //... 省略 setter 和 getter
    
        public User() {}
    
        public User(String userId, String userName) {
            this.userId = userId;
            this.userName = userName;
        }
    }
    publicclass Role {
    
        private String userId;
    
        private String roleName;
    
        //... 省略 setter 和 getter
    
        public Role(String userId, String roleName) {
            this.userId = userId;
            this.roleName = roleName;
        }
    }

    2.1、通过 JSON 进行转换

    在这里,我们利用阿里巴巴的fastjson包进行转换,通过maven引入 jar,如下:

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.49</version>
    </dependency>

    方法如下:

    publicclass BeanMapUtilByJson {
    
        /**
         * 对象转Map
         * @param object
         * @return
         */
        public static Map beanToMap(Object object){
            return JSONObject.parseObject(JSON.toJSONString(object),Map.class);
        }
    
        /**
         * map转对象
         * @param map
         * @param beanClass
         * @param <T>
         * @return
         */
        publicstatic <T> T mapToBean(Map map, Class<T> beanClass){
            return JSONObject.parseObject(JSON.toJSONString(map),beanClass);
        }
    }

    2.2、利用反射进行转换

    这种操作是利用 java 原生提供的反射特性来实现互转,方法如下:

    publicclass BeanMapUtilByReflect {
    
        /**
         * 对象转Map
         * @param object
         * @return
         * @throws IllegalAccessException
         */
        public static Map beanToMap(Object object) throws IllegalAccessException {
            Map<String, Object> map = new HashMap<String, Object>();
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                map.put(field.getName(), field.get(object));
            }
            return map;
        }
    
        /**
         * map转对象
         * @param map
         * @param beanClass
         * @param <T>
         * @return
         * @throws Exception
         */
        publicstatic <T> T mapToBean(Map map, Class<T> beanClass) throws Exception {
            T object = beanClass.newInstance();
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                int mod = field.getModifiers();
                if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
                    continue;
                }
                field.setAccessible(true);
                if (map.containsKey(field.getName())) {
                    field.set(object, map.get(field.getName()));
                }
            }
            return object;
        }
    }

    2.3、利用内省机制进行转换

    内省(Introspector)是 Java 语言对 JavaBean 类属性、事件的一种缺省处理方法,也是基于 java 原生实现,操作如下:

    publicclass BeanMapUtilByIntros {
    
        /**
         * 对象转Map
         * @param object
         * @return
         */
        public static Map beanToMap(Object object) throws Exception {
            Map<String, Object> map = new HashMap<String, Object>();
            BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (key.compareToIgnoreCase("class") == 0) {
                    continue;
                }
                Method getter = property.getReadMethod();
                Object value = getter!=null ? getter.invoke(object) : null;
                map.put(key, value);
            }
            return map;
        }
    
        /**
         * map转对象
         * @param map
         * @param beanClass
         * @param <T>
         * @return
         * @throws Exception
         */
        publicstatic <T> T mapToBean(Map map, Class<T> beanClass) throws Exception {
            T object = beanClass.newInstance();
            BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                Method setter = property.getWriteMethod();
                if (setter != null) {
                    setter.invoke(object, map.get(property.getName()));
                }
            }
            return object;
        }
    }

    2.4、利用 apache 中的 BeanUtils 进行转换

    在使用这个方法前,需要手动引入 apache 中的 beanutils 包,方法如下:

    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>1.9.3</version>
    </dependency>

    想必大家都不会陌生,操作如下:

    publicclass BeanMapUtilByApache {
    
        /**
         * 对象转Map
         * @param object
         * @return
         */
        public static Map beanToMap(Object object){
            returnnew org.apache.commons.beanutils.BeanMap(object);
        }
    
        /**
         * map转对象
         * @param map
         * @param beanClass
         * @param <T>
         * @return
         */
        publicstatic <T> T mapToBean(Map map, Class<T> beanClass) throws Exception {
            T object = beanClass.newInstance();
            org.apache.commons.beanutils.BeanUtils.populate(object, map);
            return object;
        }
    }

    2.5、利用 cglib 中的 BeanMap 进行转换

    在使用这个方法前,需要手动引入cglib 包,方法如下:

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>

    Spring 内置了 cglib,在项目中经常看到有同学用这个方法,操作如下:

    publicclass BeanMapUtilByCglib {
    
        /**
         * 对象转Map
         * @param object
         * @return
         */
        public static Map beanToMap(Object object){
            Map<String, Object> map = new HashMap<String, Object>();
            if (object != null) {
                BeanMap beanMap = BeanMap.create(object);
                for (Object key : beanMap.keySet()) {
                    map.put(key+"", beanMap.get(key));
                }
            }
            return map;
        }
    
        /**
         * map转对象
         * @param map
         * @param beanClass
         * @param <T>
         * @return
         * @throws Exception
         */
        publicstatic <T> T mapToBean(Map map, Class<T> beanClass) throws Exception {
            T bean = beanClass.newInstance();
            BeanMap beanMap = BeanMap.create(bean);
            beanMap.putAll(map);
            return bean;
        }
    }

    2.6、测试

    上面介绍完了操作,最后我们来测试一下,新建一个测试客户端TestClient,分别测试上面5个工具类

    publicclass BeanMapClient {
    
        /**
         * 测试
         * @param args
         */
        public static void main(String[] args) {
            //为了更贴近实际场景,构造嵌套实体对象
            User user = new User("1","张三");
            List<Role> roleList = new ArrayList<Role>();
            roleList.add(new Role("1","技术经理"));
            roleList.add(new Role("1","信息主任"));
            user.setRoleList(roleList);
            //bean转map
            Map userMap = BeanMapUtilByJson.beanToMap(user);
            System.out.println("转换后的map:" + JSON.toJSONString(userMap));
            //修改源对象中信息
            user.getRoleList().get(0).setRoleName("项目经理");
            user.getRoleList().add(new Role("1","项目经理"));
            //map转bean
            User newUser = BeanMapUtilByJson.mapToBean(userMap,User.class);
            System.out.println("转换后的bean:" + JSON.toJSONString(newUser));
        }
    }

    输出结果如下:

    对上面5中输出结果进行分析,可以得出如下结论:

    • 将对象转换成map,基本都一致,将map转成bean的时候,有些小区别;
    • 方法一,通过json转换后的bean对象,即使源对象发生改变,转换后的对象不会发生变化,这是因为json在底层转换bean时,都创建了新的对象;
    • 方法二、三、四、五,当源对象有嵌套对象,修改嵌套对象,也就是Role内容,转换后的对象也会随之发生改变;

    从结果可以看出,方法一、二、三、四、五,都可以进行对象与 map 的互转,那他们到底有啥区别呢?

    下面我们从性能角度来观察一下,使用for循环测试 map 与对象的互转,结果如下:

    可能每个机器的性能不一样,这个是我的电脑上测试的结果,从数据上可以看出:

    • 性能最好的是反射方法,其次就是内省方法,这两个方法都没有引用第三方jar包,所以相对可能要快点;
    • 使用第三方引用中,cglib 效率还可以,其次 apache的beanUtils工具包,最后就是Json包;

    三、总结

    如果对性能要求很高,可以采用 java 原生编程实现对象与 map 的互转,如果已经引用了 apache 或者 cglib 的jar,可以利用它提供的方法进行转换,优先推荐使用 cglib,因为 Spring 已经继承了 cglib,在项目中可以直接使用!

    原文链接:https://mp.weixin.qq.com/s/6dq6aKfV1GKLOvD-RGmcpw

  • 相关阅读:
    Xcode界面切换动画效果
    Objective—C中的排序及Compare陷阱
    串行口应用
    在windows上搭建C语言开发环境——借助eclipse和MinGW
    Leetcode--Two Sum
    C++语言笔记系列之十六——赋值兼容规则&amp;多继承的二义性
    在Powerdesigner中创建概念数据模型
    数据模型
    数据描述的三个领域
    开启PowerDesigner15工具栏上的被禁用掉的图标
  • 原文地址:https://www.cnblogs.com/yrjns/p/12373591.html
Copyright © 2011-2022 走看看