Почему не рекомендуется использовать BeanUtils.copyProperties для копирования данных | Команда JD Cloud Technology

В реальном развитии бизнеса мы часто сталкиваемся с назначениями между атрибутами объектов , такими как VO, BO, PO и DTO . Когда атрибутов много, рабочая нагрузка по присвоению значений с помощью методов get и set относительно велика, поэтому рабочая нагрузка относительно велика. Многие люди предпочтут использовать метод copyProperties BeanUtils , инструмента копирования, предоставляемого Spring , для копирования свойств между объектами. Этот метод может значительно уменьшить нашу нагрузку по написанию вручную кода назначения атрибутов объекта. Поскольку он настолько удобен, почему не рекомендуется его использовать? Ниже приведены некоторые распространенные ошибки при копировании данных BeanUtils.copyProperties, которые я скомпилировал .

1: Несовместимые типы атрибутов вызывают сбой копирования.

Эту яму можно разделить на два типа:

(1) Один и тот же атрибут имеет разные типы.

В реальной разработке весьма вероятно, что одно и то же поле имеет несовместимые типы, определенные в разных классах. Например, ID может быть определен в классе A как Long, а в классе B как String. В этом случае, если вы используете BeanUtils When copying . copyProperties , копирование завершится неудачей, в результате чего соответствующее поле станет нулевым. Соответствующие случаи следующие:

public class BeanUtilsTest {

    public static void main(String[] args) {
        SourcePoJo sourcePoJo = new SourcePoJo("jingdong", (long) 35711);
        TargetPoJo targetPoJo = new TargetPoJo();
        BeanUtils.copyProperties(sourcePoJo,targetPoJo);
        System.out.println(targetPoJo);
    }
}
@Data
@AllArgsConstructor
class SourcePoJo{
    private String username;
    private Long id;
}

@Data
class TargetPoJo{
    private String username;
    private String id;
}

Соответствующие результаты работы следующие:



Вы можете видеть, что скопированное значение поля id имеет значение null из-за несовместимых типов.

(2) В том же поле используются тип упаковки и базовый тип соответственно.

Если вы используете для поля класс-оболочку и базовый тип соответственно, возникнет исключение, когда фактическое значение не будет передано. Конкретные случаи заключаются в следующем:

public class BeanUtilsTest {

    public static void main(String[] args) {
        SourcePoJo sourcePoJo = new SourcePoJo();
        sourcePoJo.setUsername("joy");
        TargetPoJo targetPoJo = new TargetPoJo();
        BeanUtils.copyProperties(sourcePoJo,targetPoJo);
        System.out.println(targetPoJo);
    }
}
@Data
class SourcePoJo{
    private String username;
    private Long id;
}

@Data
class TargetPoJo{
    private String username;
    private long id;
}

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



Примечание. Если атрибут логического типа использует базовый тип и тип-оболочку соответственно и если имя атрибута начинается с is, например isSuccess, это также приведет к сбою копирования.

2. Перезапись нулевого значения приводит к аномалиям данных.

В ходе развития бизнеса у нас может возникнуть необходимость скопировать некоторые поля.Если некоторые поля в скопированных данных имеют нулевые значения, но значения тех же полей в соответствующих данных, которые необходимо скопировать, не равны нулю, если напрямую Когда BeanUtils.copyProperties используется для копирования данных, нулевое значение скопированных данных перезапишет поля целевых данных копирования, в результате чего исходные данные станут недействительными.

Соответствующие случаи таковы:

public class BeanUtilsTest {

    public static void main(String[] args) {
        SourcePoJo sourcePoJo = new SourcePoJo();
        sourcePoJo.setId("35711");
        TargetPoJo targetPoJo = new TargetPoJo();
        targetPoJo.setUsername("Joy");
        BeanUtils.copyProperties(sourcePoJo,targetPoJo);
        System.out.println(targetPoJo);
    }
}
@Data
class SourcePoJo{
    private String username;
    private String id;
}

@Data
class TargetPoJo{
    private String username;
    private String id;
}

Соответствующие результаты работы следующие:



Вы можете видеть, что поле имени пользователя, которое изначально имело значение в целевом результате копирования, было перезаписано на нулевое значение. Хотя вы можете использовать перегруженный метод BeanUtils.copyProperties и использовать пользовательский ConvertUtilsBean для копирования некоторых полей, это само по себе более сложно и теряет смысл использования BeanUtils.copyProperties для копирования данных, поэтому это не рекомендуется.

3. Ошибка импорта пакета приводит к некорректному копированию данных.

При использовании BeanUtils.copyProperties для копирования данных, если в проект вводятся как пакет bean-компонентов Spring, так и пакет beanutils Apache, если при импорте пакета возникает ошибка импорта, это может привести к сбою копирования данных, что непросто. обнаружить во время устранения неполадок. Обычно мы используем метод копирования в пакете Sping. Различия между ними заключаются в следующем:

//org.springframework.beans.BeanUtils(源对象在左边,目标对象在右边)
public static void copyProperties(Object source, Object target) throws BeansException 
//org.apache.commons.beanutils.BeanUtils(源对象在右边,目标对象在左边)
public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException

4: Ссылка на поле не найдена, а измененное содержимое трудно отследить.

Если во время разработки или устранения неполадок мы находим источник значения поля ( не переданного вызывающей стороной ) в ссылке, мы можем использовать полнотекстовый поиск, чтобы найти соответствующий метод назначения (например, метод set, метод сборки и т. д.). , но если данные копируются с помощью BeanUtils.copyProperties в ссылке , будет сложно быстро найти место, где присвоено значение, что приводит к низкой эффективности устранения неполадок.

5: данные внутреннего класса не могут быть успешно скопированы.

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

public class BeanUtilsTest {

    public static void main(String[] args) {
        SourcePoJo sourcePoJo = new SourcePoJo();
        sourcePoJo.setUsername("joy");
        SourcePoJo.InnerClass innerClass = new SourcePoJo.InnerClass("sourceInner");
        sourcePoJo.innerClass=innerClass;
        System.out.println(sourcePoJo.toString());
        TargetPoJo targetPoJo = new TargetPoJo();
        BeanUtils.copyProperties(sourcePoJo,targetPoJo);
        System.out.println(targetPoJo.toString());
    }
}
//下面是类的信息,这里就直接放到一块展示了
@Data
@ToString
public class SourcePoJo{
    private String username;
    private Long id;
    public InnerClass innerClass;
    @Data
    @ToString
    @AllArgsConstructor
    public static class InnerClass{
        public String innerName;
    }
}

@Data
@ToString
public class TargetPoJo{
    private String username;
    private Long id;
    public InnerClass innerClass;
    @Data
    @ToString
    public static class InnerClass{
        public String innerName;
    }
}

Вот результаты:



В приведенном выше случае в источнике копирования и цели копирования есть внутренний класс InnerClass. Хотя атрибуты внутреннего класса одинаковы и имя класса одинаковое, они находятся в разных классах, поэтому Spring будет думать, что атрибуты разные. и не будет копировать данные.

6: BeanUtils.copyProperties — это неполная копия.

Здесь я сначала рассмотрю глубокий и поверхностный текст для всех.

Неглубокое копирование означает создание нового объекта, который имеет те же значения свойств, что и исходный объект, но по-прежнему использует ту же ссылку для свойств ссылочных типов. То есть при поверхностном копировании, когда значение атрибута ссылки исходного контента изменяется, значение атрибута ссылки скопированного объекта также изменится соответствующим образом.

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

Ниже приведен соответствующий пример кода:

public class BeanUtilsTest {

    public static void main(String[] args) {
        Person sourcePerson = new Person("sunyangwei",new Card("123456"));
        Person targetPerson = new Person();
        BeanUtils.copyProperties(sourcePerson, targetPerson);
        sourcePerson.getCard().setNum("35711");
        System.out.println(targetPerson);
    }
}


@Data
@AllArgsConstructor
class Card {
    private String num;
}

@NoArgsConstructor
@AllArgsConstructor
@Data
class Person {
    private String name;
    private Card card;
}

Вот результаты:



Резюме: Из результатов выполнения кода мы можем обнаружить, что после изменения данных ссылочного типа исходного объекта после копирования значение скопированных данных станет ненормальным.Проблему такого рода также трудно устранить.

7. Базовая реализация представляет собой рефлексивное копирование с низкой эффективностью.

Нижний уровень BeanUtils.copyProperties предназначен для получения методов set и get объекта посредством отражения, а затем завершения копирования данных с помощью get и set. Общая эффективность копирования низкая.

Ниже приведено сравнение эффективности копирования данных с помощью BeanUtils.copyProperties и прямой установки значения.Чтобы наглядно увидеть эффект, приведем пример копирования 10 000 раз:

public class BeanUtilsTest {

    public static void main(String[] args) {
        long copyStartTime = System.currentTimeMillis();
        User sourceUser = new User("sunyangwei");
        User targetUser = new User();
        for(int i = 0; i < 10000; i++) {
            BeanUtils.copyProperties(sourceUser, targetUser);
        }
        System.out.println("copy方式:"+(System.currentTimeMillis()-copyStartTime));

        long setStartTime = System.currentTimeMillis();
        for(int i = 0; i < 10000; i++) {
            targetUser.setUserName(sourceUser.getUserName());
        }
        System.out.println("set方式:"+(System.currentTimeMillis()-setStartTime));
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class User{
    private String userName;
}

Ниже приводится сравнение результатов эффективности выполнения:



Можно обнаружить, что разрыв в производительности между обычным набором и BeanUtils.copyProperties очень велик. Поэтому используйте BeanUtils.copyProperties с осторожностью.

Выше приведены распространенные ошибки при использовании BeanUtils.copyProperties для копирования данных. Большинство этих ошибок относительно скрыты, и их трудно устранить в случае возникновения проблем. Поэтому не рекомендуется использовать BeanUtils.copyProperties для копирования данных в бизнесе. Любые недостатки в статье приветствуются, дополняемые и исправляемые.

Автор: JD Technology Сунь Янвэй

Источник: Сообщество разработчиков JD Cloud. При перепечатке указывайте источник.

Автор известного проекта с открытым исходным кодом потерял работу из-за мании - «Искать деньги в Интернете» No Star, No Fix 2023 Выпущена десятка лучших в мире инженерных достижений: ChatGPT, Hongmeng Operating System, China Space Station и другие избранные ByteDance были «запрещены» OpenAI Google анонсирует самое популярное расширение Chrome в 2023 году Академик Ни Гуаннань: Надеюсь, отечественный SSD заменит импортный HDD для разблокировки мобильного телефона Xiaomi BL? Сначала задайте вопрос на собеседовании с Java-программистом. Компания Arm уволила более 70 китайских инженеров и запланировала реорганизовать свой китайский бизнес по разработке программного обеспечения. OpenKylin 2.0 раскрывает | UKUI 4.10 дизайн с двойным ромбом, красивый и качественный! Выпущена версия Manjaro 23.1 под кодовым названием «Вулкан».
{{o.name}}
{{m.name}}

рекомендация

отmy.oschina.net/u/4090830/blog/10326285