Эта статья перепечатана с https://pjmike.github.io/2018/11/03/Bean-отображение-инструменты-Apache-BeanUtils-VS-Spring-BeanUtils/, автор pjmike.
В процессе разработки наших реальных проектов мы часто сталкиваемся с необходимостью копирования атрибутов между двумя различными объектами, чтобы использовать информацию об атрибутах исходного объекта для последующих действий, не изменяя при этом исходные атрибуты объекта. Например, DTO (Data Transfer Object) и объект данных DO (Data Object). Нам нужно скопировать атрибуты объекта DO в DTO, но форматы объектов различны, поэтому нам необходимо писать код для отображения, чтобы преобразовать значения атрибутов объекта из одного типа в другой.
Перед тем как подробно рассмотреть два инструмента BeanUtils, давайте сначала обсудим некоторые базовые знания. Эти два инструмента по сути являются инструментами копирования объектов, а копирование объектов может быть глубоким или поверхностным. Давайте рассмотрим это подробнее.
Кратко:
Ранее мы кратко обсудили некоторые базовые знания о копировании объектов. Теперь давайте подробно рассмотрим два инструмента BeanUtils.### Библиотека BeanUtils Apache
Давайте рассмотрим очень простой пример использования BeanUtils.```java public class PersonSource { private Integer id; private String username; private String password; private Integer age; // getters/setters опущены } public class PersonDest { private Integer id; private String username; private Integer age; // getters/setters опущены } public class TestApacheBeanUtils { public static void main(String[] args) throws InvocationTargetException, IllegalAccessException { // Просто для отдельного тестирования PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21); PersonDest personDest = new PersonDest(); BeanUtils.copyProperties(personDest, personSource); System.out.println("persondest: " + personDest); } } persondest: PersonDest{id=1, username='pjmike', age=21}
```java
// Копирование значений из исходного объекта в целевой объект
public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
BeanUtilsBean.getInstance().copyProperties(dest, orig);
}
По умолчанию, использование org.apache.commons.beanutils.BeanUtils
для копирования сложных объектов является ссылкой, что означает поверхностное копирование.
Однако из-за низкой производительности копирования объектов в библиотеке BeanUtils Apache, ее не рекомендуется использовать. В плагине Java-разработки Alibaba также указано:
Ali-Check | Избегайте использования Apache Beanutils для копирования свойств.
Библиотека commons-beanutils добавляет много проверок при копировании объектов, включая преобразование типов и проверку доступности классов объектов, что делает ее сложной и медленной. Конкретная реализация кода приведена ниже:
public void copyProperties(final Object dest, final Object orig)
throws IllegalAccessException, InvocationTargetException {
``` // Проверка существования указанных объектов
if (dest == null) {
throw new IllegalArgumentException("Не указан объект назначения");
}
if (orig == null) {
throw new IllegalArgumentException("Не указан объект источника");
}
if (log.isDebugEnabled()) {
log.debug("BeanUtils.copyProperties(" + dest + ", " + orig + ")");
}
}
// Копирование свойств, преобразуя при необходимости
if (orig instanceof DynaBean) {
final DynaProperty[] origDescriptors = ((DynaBean) orig).getDynaClass().getDynaProperties();
for (DynaProperty origDescriptor : origDescriptors) {
final String name = origDescriptor.getName();
// Необходимо проверить isReadable() для WrapDynaBean
// (см. Jira issue# BEANUTILS-61)
if (getPropertyUtils().isReadable(orig, name) && getPropertyUtils().isWriteable(dest, name)) {
final Object value = ((DynaBean) orig).get(name);
copyProperty(dest, name, value);
}
}
} else if (orig instanceof Map) {
@SuppressWarnings("unchecked")
final // Свойства Map всегда имеют тип <String, Object>
Map<String, Object> propMap = (Map<String, Object>) orig;
for (final Map.Entry<String, Object> entry : propMap.entrySet()) {
final String name = entry.getKey();
if (getPropertyUtils().isWriteable(dest, name)) {
copyProperty(dest, name, entry.getValue());
}
}
} else /* if (orig is a standard JavaBean) */ {
final PropertyDescriptor[] origDescriptors = getPropertyUtils().getPropertyDescriptors(orig);
for (PropertyDescriptor origDescriptor : origDescriptors) {
final String name = origDescriptor.getName();
if ("class".equals(name)) {
continue; // Нет смысла пытаться установить класс объекта
}
if (getPropertyUtils().isReadable(orig, name) &&
getPropertyUtils().isWriteable(dest, name)) {
try {
final Object value =
getPropertyUtils().getSimpleProperty(orig, name);
copyProperty(dest, name, value);
} catch (final NoSuchMethodException e) {
// Не должно произойти
}
}
}
}```java
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
@Nullable String... ignoreProperties) throws BeansException {
Assert.notNull(source, "Источник не может быть null");
Assert.notNull(target, "Цель не может быть null");
}
Использование BeanUtils из Spring для копирования объектов:
public class TestSpringBeanUtils {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
// Просто для отдельного тестирования
PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21);
PersonDest personDest = new PersonDest();
BeanUtils.copyProperties(personSource, personDest);
System.out.println("persondest: " + personDest);
}
}
BeanUtils из Spring также использует метод copyProperties
для копирования, но его реализация очень проста — это просто получение и установка значений для свойств с одинаковыми именами в двух объектах, проверка доступности свойств. Конкретная реализация приведена ниже:
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
@Nullable String... ignoreProperties) throws BeansException {
Assert.notNull(source, "Источник не может быть null");
Assert.notNull(target, "Цель не может быть null");
}
``` Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Класс цели [" + target.getClass().getName() +
"] не может быть приведен к типу редактируемого класса [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
}
``````markdown
для (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
если (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
если (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
если (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
попытаться {
если (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
если (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
бросить new FatalBeanException(
"Не удалось скопировать свойство '" + targetPd.getName() + "' из источника в целевую переменную", ex);
}
}
}
}
}
``````Увидеть можно, что присваивание членов переменных основано на списке членов целевого объекта и пропускает игнорируемые члены переменных, а также те, которые отсутствуют в исходном объекте. Поэтому этот метод безопасен и не приводит к ошибкам из-за структурных различий между двумя объектами, но **должны быть одинаковыми типы членов переменных с одинаковыми именами**```## Краткое резюме
Вышеупомянутый краткий анализ двух BeanUtils показывает, что BeanUtils из Apache имеет низкую производительность и не рекомендуется к использованию. Вместо этого можно использовать BeanUtils из Spring или другие копировальные фреймворки, такие как **[Dozer](http://dozer.sourceforge.net/documentation/gettingstarted.html)**, **[ModelMapper](http://modelmapper.org/)** и т.д. В последующих статьях я рассмотрю эти копировальные фреймворки.
## Ссылки и благодарности
- [Разговор о копировании объектов в Java-разработке](http://www.importnew.com/26306.html)
- [Подробное объяснение глубокого и поверхностного копирования в Java](https://segmentfault.com/a/1190000010648514)
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )