BeanUtil.copyProperties()进行属性拷贝时如何忽略NULL值——CopyOptions配置详解
一、需求背景
在日常Java开发中,我么通常会使用 BeanUtils
的 copyProperties()
方法来实现对象属性复制。然而在实际业务场景中,我们经常会遇到这样的需求:
当源对象属性值为NULL时,不希望覆盖目标对象原有的非NULL值。例如:
// 用户更新信息场景
UserDTO userDTO = new UserDTO();
userDTO.setName("张三");
userDTO.setAge(null); // 年龄字段为nullUser targetUser = userService.getUserById(1);
// 原始targetUser.age=25
BeanUtils.copyProperties(userDTO, targetUser);
// 此时targetUser.age会被覆盖为null,不符合预期
二、CopyOptions核心配置解析
2.1 CopyOptions概述
在Hutool工具包的BeanUtil中,copyProperties()方法提供了更灵活的CopyOptions参数来控制拷贝行为:
public static void copyProperties(Object source, Object target, CopyOptions copyOptions)
CopyOptions
采用建造者模式设计,主要包含以下配置项:
配置项 | 类型 | 默认值 | 说明 |
---|---|---|---|
ignoreNullValue | boolean | false | 是否忽略NULL值 |
ignoreCase | boolean | false | 是否忽略字段名称大小写 |
ignoreError | boolean | false | 是否忽略拷贝错误 |
editable | Class<?> | null | 限制目标对象类型 |
ignoreProperties | String[] | null | 要忽略的属性名数组 |
override | boolean | true | 是否覆盖已有值 |
fieldMapping | Map<String, String> | null | 字段名称映射关系 |
2.2 忽略NULL值的配置方法
要实现忽略NULL值的拷贝,需要创建CopyOptions并设置 ignoreNullValue
为true:
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.CopyOptions;// 创建配置
CopyOptions copyOptions = CopyOptions.create().setIgnoreNullValue(true); // 关键配置// 执行拷贝
BeanUtil.copyProperties(source, target, copyOptions);
三、深度配置示例
3.1 复合配置示例
// 复杂场景配置示例
CopyOptions options = CopyOptions.create().setIgnoreNullValue(true) // 忽略null值.setIgnoreCase(true) // 忽略字段大小写.setIgnoreProperties("password") // 忽略密码字段.setOverride(false) // 不覆盖已有值.setFieldMapping(Collections.singletonMap("userName", "name")); // 字段映射BeanUtil.copyProperties(dto, entity, options);
3.2 与Spring框架的对比
Spring的BeanUtils.copyProperties()默认不支持忽略NULL值,需要自行扩展:
// Spring场景下的NULL值忽略实现
public class CustomBeanUtils extends BeanUtils {public static void copyPropertiesIgnoreNull(Object src, Object target) {PropertyDescriptor[] srcPds = getPropertyDescriptors(src.getClass());for (PropertyDescriptor srcPd : srcPds) {if (srcPd.getReadMethod() != null) {try {Object value = srcPd.getReadMethod().invoke(src);if (value != null) { // 只拷贝非NULL值PropertyDescriptor targetPd = getPropertyDescriptor(target.getClass(), srcPd.getName());if (targetPd != null && targetPd.getWriteMethod() != null) {targetPd.getWriteMethod().invoke(target, value);}}} catch (Exception ex) {throw new RuntimeException(ex);}}}}
}
四、特殊场景处理
4.1 嵌套对象拷贝
对于嵌套对象的NULL值处理,需要递归配置:
public static void deepCopyIgnoreNull(Object source, Object target) {CopyOptions options = CopyOptions.create().setIgnoreNullValue(true);BeanUtil.copyProperties(source, target, options);// 获取所有嵌套对象字段Field[] fields = source.getClass().getDeclaredFields();for (Field field : fields) {if (field.getType().getName().startsWith("com.yourpackage")) {try {field.setAccessible(true);Object srcChild = field.get(source);Object targetChild = field.get(target);if (srcChild != null && targetChild != null) {deepCopyIgnoreNull(srcChild, targetChild);}} catch (IllegalAccessException e) {// 异常处理}}}
}
4.2 集合属性拷贝
// 集合属性拷贝示例
CopyOptions options = CopyOptions.create().setIgnoreNullValue(true);List<SourceDTO> sourceList = ...;
List<TargetVO> targetList = new ArrayList<>();for (SourceDTO source : sourceList) {TargetVO target = new TargetVO();BeanUtil.copyProperties(source, target, options);targetList.add(target);
}
通过合理配置CopyOptions,可以极大提升对象属性拷贝的灵活性和安全性,满足各种业务场景的需求。