后端开发必知:BeanUtils 如何在 DTO 与 VO 间巧妙复制数据

20 阅读2分钟

作为一名后端开发者,我们深知数据安全的重要性,尤其是像手机号、身份证号等关键数据

为了防止关键数据泄露,JAVA领域,基本都采用了 VO(Value Object)和 DTO(Data Transfer Object)的模式,VO(Value Object)通常只会回传最关键的属性,而DTO(Data Transfer Object)一般则包含表的所有字段数据。因此,DTO和VO之间通常需要进行关键字段的数据复制。今天,我们就来分享一个工具类——BeanUtils,并剖析其数据复制的源码。

一、BeanUtils工具类

BeanUtils是Spring框架提供的用于处理JavaBean的静态工具方法类,如实例化bean、检查bean属性类型、复制bean属性等。

1. 核心方法

  • instantiateClass(Class clazz):根据给定的类创建一个新的实例。
  • copyProperties(Object source, Object target, String... ignoreProperties):将一个对象的属性值复制到另一个对象中,可以指定忽略的属性。
  • findPropertyType(String propertyName, @Nullable Class<?>... beanClasses):获取指定Bean属性的类型。

2. 数据复制场景

想象一下这样的场景:我们从数据库中获取了完整的用户数据,存储在 DTO 中。但是,当我们要将这些数据展示给前端页面时,我们并不希望将所有的字段都暴露出去,只需要传递一些关键的信息。这时,我们就可以利用 BeanUtils 工具类,将 DTO 中这些关键的属性复制到 VO 中,然后将 VO 传递给前端。这样,既保证了数据的安全性,又提高了数据传输的效率

二、copyProperties方法分析

copyProperties方法的核心可总结为两步。

  1. 获取目标对象及其getter和setter方法
Class<?> actualEditable = target.getClass();
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
  1. 遍历目标对象的所有属性,并通过反射机制从源对象中获取对应属性的值,最后赋值给目标对象
for (PropertyDescriptor targetPd : targetPds) {
			Method writeMethod = targetPd.getWriteMethod();
			// 确保write方法存在
			if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null) {
					Method readMethod = sourcePd.getReadMethod();
					// 确保read方法存在
					if (readMethod != null &&
							ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
						try {
						// 设置读方法的可访问性
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							// 获取source类对应属性的值
							Object value = readMethod.invoke(source);
							// 设置写方法的可访问性
							if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
								writeMethod.setAccessible(true);
							}
							// 赋值
							writeMethod.invoke(target, value);
						}
						catch (Throwable ex) {
							throw new FatalBeanException(
									"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
						}
					}
				}
			}
		}

通过上述步骤,copyProperties方法能够有效地实现对象间属性值的复制,同时支持忽略指定属性,从而满足不同场景下的需求。

三、总结

BeanUtils类的copyProperties方法,在保证数据安全的前提下,高效地实现了 DTO 和 VO 之间的数据复制。在实际开发中,我们要充分理解和掌握它的原理和使用方法,同时注意可能出现的问题,如属性名称匹配、类型兼容性((《MyBatis 数据表与实体映射的隐藏陷阱》))和性能优化等