简介
说明
本文用示例介绍Spring(SpringBoot)如何使用BeanUtils拷贝对象属性(忽略空值)。
BeanUtils类所在的包
有两个包都提供了BeanUtils类:
- Spring的(推荐):org.springframework.beans.BeanUtils
- Apache的:org.apache.commons.beanutils.BeanUtils
为什么要忽略空值拷贝?
- 默认情况下,BeanUtils会覆盖数据
- 在拷贝属性时,如果源数据的某个字段是null,会把目标数据的值给覆盖为null
忽略null值拷贝属性的用法
BeanUtils.copyProperties(Object source, Object target, String... ignoreProperties)
注意:由于BeanUtils这个方法是浅拷贝,所以本文不考虑对象里边有其他对象的这种情况。
获取null属性名(工具类)
见:SpringBoot-获得对象属性为空值的属性名 – 自学精灵
实例
本处为了全面,将以下情况都考虑进去:
- 继承了某个类
代码结构

Entity
公共Entity
package com.knife.example.common.entity;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 公共实体类
*/
@Data
public class CommonEntity {
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 修改时间
*/
private LocalDateTime updateTime;
}
ProductVO
package com.example.entity;
import lombok.Data;
@Data
public class User {
private Long id;
private String userName;
private String nickName;
// 0:正常 1:被锁定
private Integer status;
}
工具类
package com.knife.example.common.util;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.StringUtils;
import java.beans.PropertyDescriptor;
import java.util.HashSet;
import java.util.Set;
public class PropertyUtil {
public static String[] readEmptyPropertyNames(Object source) {
BeanWrapper src = new BeanWrapperImpl(source);
PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<>();
for (PropertyDescriptor pd : pds) {
String filedName = pd.getName();
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) {
emptyNames.add(filedName);
} else {
if (srcValue instanceof String) {
if (!StringUtils.hasText((String) srcValue)) {
emptyNames.add(filedName);
}
}
}
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}
}
Controller
package com.knife.example.business.product.controller;
import com.knife.example.business.product.vo.ProductVO;
import com.knife.example.common.util.PropertyUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.Arrays;
@Api(tags = "商品")
@RestController
@RequestMapping("product")
public class ProductController {
@ApiOperation("查询详情")
@GetMapping("detail")
public ProductVO detail(Long id) {
// 源对象
ProductVO productVO = new ProductVO();
productVO.setId(10L);
productVO.setName("");
productVO.setStockQuantity(null);
productVO.setCreateTime(LocalDateTime.now());
productVO.setUpdateTime(null);
System.out.println("------------------------------------------------");
// 目标对象
ProductVO productVOTarget = new ProductVO();
productVOTarget.setId(9L);
productVOTarget.setName("键盘");
productVOTarget.setStockQuantity(150);
productVOTarget.setCreateTime(LocalDateTime.now().minusHours(3));
productVOTarget.setUpdateTime(LocalDateTime.now().minusHours(3));
// 默认不忽略空值
BeanUtils.copyProperties(productVO, productVOTarget);
//读出空值字段名
// String[] emptyPropertyNames = PropertyUtil.readEmptyPropertyNames(productVO);
// System.out.println("空值字段名:" + Arrays.toString(emptyPropertyNames));
// //忽略空值拷贝
// BeanUtils.copyProperties(productVO, productVOTarget, emptyPropertyNames);
return productVOTarget;
}
}
测试1:默认情况

结论
- 默认情况下,null和空字符串会覆盖目标对象的属性
测试2:忽略空值拷贝
修改一下Controller,改为如下:
package com.knife.example.business.product.controller;
import com.knife.example.business.product.vo.ProductVO;
import com.knife.example.common.util.PropertyUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.Arrays;
@Api(tags = "商品")
@RestController
@RequestMapping("product")
public class ProductController {
@ApiOperation("查询详情")
@GetMapping("detail")
public ProductVO detail(Long id) {
// 源对象
ProductVO productVO = new ProductVO();
productVO.setId(10L);
productVO.setName("");
productVO.setStockQuantity(null);
productVO.setCreateTime(LocalDateTime.now());
productVO.setUpdateTime(null);
// 目标对象
ProductVO productVOTarget = new ProductVO();
productVOTarget.setId(9L);
productVOTarget.setName("键盘");
productVOTarget.setStockQuantity(150);
productVOTarget.setCreateTime(LocalDateTime.now().minusHours(3));
productVOTarget.setUpdateTime(LocalDateTime.now().minusHours(3));
// 默认不忽略空值
// BeanUtils.copyProperties(productVO, productVOTarget);
//读出空值字段名
String[] emptyPropertyNames = PropertyUtil.readEmptyPropertyNames(productVO);
System.out.println("空值字段名:" + Arrays.toString(emptyPropertyNames));
//忽略空值拷贝
BeanUtils.copyProperties(productVO, productVOTarget, emptyPropertyNames);
return productVOTarget;
}
}
结果:

结论:
成功的避免了空值覆盖。

请先 !