一看就懂!Java利用反射實現快速Bean屬性賦值(淺克隆、深克隆)

在開發中經常需要將PO、VO、DTO、DO相互轉換,如果一個個set將十分麻煩,現在也有很成熟的轉換工具類,例如dozer,本人日常開發也會使用這個工具。出於技癢,於是自己開始研究利用反射實現轉換工具。

2020/3/10版:

 


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 利用反射原理的對象轉換類
 */
public class BeanUtils {
    private static final String GET = "get";
    private static final String SET = "set";

    /**
     * 淺複製
     *
     * @param from 轉換對象
     * @param to   目標對象
     * @param <T>  返回對象
     * @return
     */
    public static <T> T covert(T from, T to) {
        System.out.println("-----------開始轉換----------");
        Class<?> fromClass = from.getClass();
        Class<?> toClass = to.getClass();
        Method[] toClassMethods = toClass.getMethods();
        //遍歷to含有的方法
        for (Method method : toClassMethods) {
            String methodName = method.getName();
            // 如果該方法是set方法
            if (methodName.startsWith("set")) {
                try {
                    // 從from 獲取對應的get方法
                    Method getMethod = fromClass.getDeclaredMethod(methodName.replace("set", "get"));
                    // 執行get方法獲取from的值
                    Object value = getMethod.invoke(from);
                    // 執行set方法設置to的值
                    method.invoke(to, value);

                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("-----------轉換完成----------");
        return to;
    }


}

 

    public static void main(String[] args) {

        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);

        UserEntity userEntity = new UserEntity();
        userEntity.setId(1);
        userEntity.setName("張三");
        userEntity.setPassword("123admin");
        userEntity.setSex("男");
        userEntity.setUsername("user");
        userEntity.setList(list);
        System.out.println(userEntity);
        UserVO covert = (UserVO) BeanUtils.covert(userEntity, new UserVO());
        System.out.println(covert);

        list.add(4);
        System.out.println("------------修改原本的list後---------");
        System.out.println(covert);
    }

輸出: 

這個版本是我的第一版,目前實現了屬性的淺複製,可以看到例子中list如果後面修改,會影響到轉換後vo的list屬性,原因是隻實現了淺複製。本來想利用clone克隆實現深複製的。但是發現 Object value= getMethod.invoke(from); 我想不出如何調用將value強轉回原本類型,唉還是對反射機制不夠了解....待了解後在進行進一步補充。但是日常開發中大多數不會出現轉換之後還需要修改原本pojo屬性的場景。

 

2020/3/25

這裏採用了取巧的方式,直接將原本的對象先深克隆,就不需要一步步判斷是否需要對當前值進行克隆。

深克隆的方法有兩種:1.序列化與反序列化生成對象。2.所有引用類型都得實現Cloneable。

這次採用了序列化與反序列化的思路,實現了對象的深克隆。(因爲序列化的話比較現實,所有的引用類型都得實現Cloneable就太麻煩了)

//實現序列化接口
public class UserEntity implements Serializable {

    private Integer id;
    private String username;
    private String password;
    private String name;
    private String sex;
    private List<Integer> list;
}
    /**
     * 深拷貝
     *
     * @param from
     * @param to
     * @param <T>
     * @return
     */
    public static <T> T depthCovert(T from, T to) {
        Class<?> fromClass = from.getClass();
        Class<?> toClass = to.getClass();

        System.out.println("-----------開始轉換----------");
        System.out.println("被轉換的對象類型" + fromClass);
        System.out.println("待轉換的對象類型" + toClass);
        if (!(fromClass instanceof Serializable)) {
            throw new RuntimeException("轉換對象沒有實現Serializable");
        }


        try {
            from = deepClone(from);
        } catch (Exception e) {
            throw new RuntimeException("深拷貝對象異常");
        }


        Method[] toClassMethods = toClass.getMethods();
        //遍歷to含有的方法
        for (Method method : toClassMethods) {
            String methodName = method.getName();
            // 如果該方法是set方法
            if (methodName.startsWith("set")) {
                try {
                    // 從from 獲取對應的get方法
                    Method getMethod = fromClass.getDeclaredMethod(methodName.replace("set", "get"));
                    // 執行get方法獲取from的值
                    Object invoke = getMethod.invoke(from);
                    // 執行set方法設置to的值
                    method.invoke(to, invoke);

                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("-----------轉換完成----------");
        return to;
    }


    private static <T> T deepClone(T t) throws IOException, ClassNotFoundException {
        
        System.out.println("-----------開始克隆----------");
        
        // 序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(t);
        // 反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        t = (T) ois.readObject();
        System.out.println("-----------結束克隆----------");
        return t;
    }

可以看到,userVO裏的list和原本的list已經不是同一個List了,深拷貝成功。


有什麼問題可以評論或者私信我,每日在線解(LIAO)疑(SAO)。

我是大誌,一位準備996的卑微碼農🐶,覺得好用記得點贊收藏!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章