Как BeanUtils копирует список?

Spring
Как BeanUtils копирует список?

1. Предпосылки

мы вDO,Model,VOДанные могут часто преобразовываться между данными слоя:

  1. EntityСоответствует структуре данных уровня сохраняемости (обычно модели отображения таблицы базы данных);
  2. ModelСоответствует структуре данных бизнес-уровня;
  3. VOэтоControllerСтруктура данных, которая взаимодействует с клиентом.

Например: информация о пользователе (модель отображения таблицы), запрашиваемая из базы данных,UserDO, но нам нужно передать клиентуUserVO, то необходимоUserDOСвойства экземпляра назначаются одно за другимUserVOв экземпляре.

Большое количество свойств может совпадать или не совпадать между этими структурами данных.

2. Копирование данных

2.1, модель данных

  • UserDO.java
@Data
public class UserDO {

    private Long userId;
    private String userName;
    private Integer age;
    private Integer sex;
    public UserDO() {

    }

    public UserDO(Long userId, String userName, Integer age, Integer sex) {
        this.userId = userId;
        this.userName = userName;
        this.age = age;
        this.sex = sex;
    }
}
  • UserVO.java
@Data
public class UserVO {
    private Long userId;
    private String userName;
    private Integer age;
    private String sex;
}

Уведомление: UserDO.javaиUserVO.javaпоследнее полеsexТипы бывают разные, это:Integer/String

2.2. Регулярное использование-BeanUtils

Springпри условииorg.springframework.beans.BeanUtilsкласс для быстрого задания.

Например: мы запрашиваем базу данныхUserDO.javaскопировать вUserVO.java

@Test
public void commonCopy() {
    UserDO userDO = new UserDO(1L, "Van", 18, 1);
    UserVO userVO = new UserVO();
    BeanUtils.copyProperties(userDO, userVO);
    log.info("userVO:{}",userVO);
}

печать журнала:

.... userVO:UserVO(userId=1, userName=Van, age=18, sex=null)

Распечатав результаты, мы можем обнаружить, что: в дополнение к различным типамsex, остальные значения успешно копируются.

2.3 Копия коллекции

Просто скопировали объект, но иногда мы хотим скопировать группуUerDO.java, когда это набор, его нельзя напрямую присвоить таким образом. Если вы все еще следуете этой логике, следующим образом:

@Test
public void listCopyFalse() {
    List<UserDO> userDOList = new ArrayList();
    userDOList.add(new UserDO(1L, "Van", 18, 1));
    userDOList.add(new UserDO(2L, "VanVan", 18, 2));
    List<UserVO> userVOList = new ArrayList();
    BeanUtils.copyProperties(userDOList, userVOList);
    log.info("userVOList:{}",userVOList);
}

Журнал печатается следующим образом:

.... userVOList:[]

Из лога видно, что прямое копирование коллекции недопустимо, так как это решить?

3. Коллекционный экземпляр

3.1 Насильственное копирование (не рекомендуется)

Пройдитесь по коллекции, которую необходимо скопировать, и скопируйте ее принудительно.

  • Метод испытания
@Test
public void listCopyCommon() {
    List<UserDO> userDOList = new ArrayList();
    userDOList.add(new UserDO(1L, "Van", 18, 1));
    userDOList.add(new UserDO(2L, "VanVan", 20, 2));
    List<UserVO> userVOList = new ArrayList();
    userDOList.forEach(userDO ->{
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userDO, userVO);
        userVOList.add(userVO);
    });
    log.info("userVOList:{}",userVOList);
}
  • скопировать результат
.... userVOList:[UserVO(userId=1, userName=Van, age=18, sex=null), UserVO(userId=2, userName=VanVan, age=20, sex=null)]

Этот способ хоть и решаемый, но совсем не изящный, особенно хлопотный в написании.

3.2, изящная копия (В этой статье рекомендуется)

пройти черезJDK 8оболочка функционального интерфейсаorg.springframework.beans.BeanUtils

  • определить функциональный интерфейс

Функциональные интерфейсы могут содержать методы по умолчанию, здесь мы определяем методы обратного вызова по умолчанию.

@FunctionalInterface
public interface BeanCopyUtilCallBack <S, T> {

    /**
     * 定义默认回调方法
     * @param t
     * @param s
     */
    void callBack(S t, T s);
}
  • Инкапсулирует класс инструмента копирования данных.BeanCopyUtil.java
public class BeanCopyUtil extends BeanUtils {

    /**
     * 集合数据的拷贝
     * @param sources: 数据源类
     * @param target: 目标类::new(eg: UserVO::new)
     * @return
     */
    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {
        return copyListProperties(sources, target, null);
    }


    /**
     * 带回调函数的集合数据的拷贝(可自定义字段拷贝规则)
     * @param sources: 数据源类
     * @param target: 目标类::new(eg: UserVO::new)
     * @param callBack: 回调函数
     * @return
     */
    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target, BeanCopyUtilCallBack<S, T> callBack) {
        List<T> list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T t = target.get();
            copyProperties(source, t);
            list.add(t);
            if (callBack != null) {
                // 回调
                callBack.callBack(source, t);
            }
        }
        return list;
    }
}
  • Простой тест на копирование
@Test
public void listCopyUp() {
    List<UserDO> userDOList = new ArrayList();
    userDOList.add(new UserDO(1L, "Van", 18, 1));
    userDOList.add(new UserDO(2L, "VanVan", 20, 2));
    List<UserVO> userVOList = BeanCopyUtil.copyListProperties(userDOList, UserVO::new);
    log.info("userVOList:{}",userVOList);
}

распечатать результат:

.... userVOList:[UserVO(userId=1, userName=Van, age=18, sex=null), UserVO(userId=2, userName=VanVan, age=20, sex=null)]

С помощью вышеуказанного метода мы в основном реализуем копию коллекции, но из возвращаемых результатов мы можем найти:Поля с разными атрибутами не могут быть скопированы. Поэтому нам нужно реализовать пользовательское преобразование с помощью только что определенного здесь метода обратного вызова.

  • класс перечисления пола
public enum SexEnum {
    UNKNOW("未设置",0),
    MEN("男生", 1),
    WOMAN("女生",2),

    ;
    private String desc;
    private int code;

    SexEnum(String desc, int code) {
        this.desc = desc;
        this.code = code;
    }

    public static SexEnum getDescByCode(int code) {
        SexEnum[] typeEnums = values();
        for (SexEnum value : typeEnums) {
            if (code == value.getCode()) {
                return value;
            }
        }
        return null;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}
  • Копировать с функцией обратного вызова
@Test
public void listCopyUpWithCallback() {
    List<UserDO> userDOList = new ArrayList();
    userDOList.add(new UserDO(1L, "Van", 18, 1));
    userDOList.add(new UserDO(2L, "VanVan", 20, 2));
    List<UserVO> userVOList = BeanCopyUtil.copyListProperties(userDOList, UserVO::new, (userDO, userVO) ->{
        // 这里可以定义特定的转换规则
        userVO.setSex(SexEnum.getDescByCode(userDO.getSex()).getDesc());
    });
    log.info("userVOList:{}",userVOList);
}

распечатать результат:

... userVOList:[UserVO(userId=1, userName=Van, age=18, sex=男生), UserVO(userId=2, userName=VanVan, age=20, sex=女生)]

Распечатав результаты, можно обнаружить, чтоUserDO.javaсерединаIntegerТипsexскопировать вUserVO.javaсталStringТипы мальчиков/девочек.

4. Технический обмен

Исходный код на гитхабе

  1. Пыльный блог: https://www.dustyblog.cn
  2. Блог Пыли - Самородки
  3. Пыль Блог - Блог Парк
  4. Github
  5. публика
    风尘博客