對象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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM