拷贝对象通常可以选择使用 Spring 或者 Apache 的 BeanUtils.copyProperties(Object source, Object target) 方法,将 source 对象中的所有属性拷贝到 target 对象中去。
应用场景,比如更新用户。
举例如下,编辑用户页面,可以修改用户名、密码、昵称Email和状态等字段,但不可以修改注册时间,上一次登录时间,上一次登录ip 等字段。
要正确更新用户的信息,保证用户信息不丢失。我们有两种方法。
方法一、设置隐藏域
这种方法,不是很好,因为如果无需修改的字段很多,那就要写很多,容易遗漏。
方法二、从数据库取出 targetUser 对象,然后将表单的里的 User 对象拷贝到 targetUser 中
但是,我们通过 Debug 发现,最终 targetUser 对象和 User 对象一模一样,即 targetUser 中原来有值的被覆盖为 null 了。
如果使为 source 对象中为 null 的字段不拷贝呢?
答案是 重写 copyProperties 方法。
然后导入我们重写的 BeanUtils 类,使用我们自定义的 copyProperties() 方法,成功解决。
应用场景,比如更新用户。
举例如下,编辑用户页面,可以修改用户名、密码、昵称Email和状态等字段,但不可以修改注册时间,上一次登录时间,上一次登录ip 等字段。
要正确更新用户的信息,保证用户信息不丢失。我们有两种方法。
方法一、设置隐藏域
- <input type="hidden" name="createTime" th:value="${user.createTime}">
- <input type="hidden" name="lastLoginTime" th:value="${user.lastLoginTime}">
- <input type="hidden" name="lastLoginIp" th:value="${user.lastLoginIp}">
这种方法,不是很好,因为如果无需修改的字段很多,那就要写很多,容易遗漏。
方法二、从数据库取出 targetUser 对象,然后将表单的里的 User 对象拷贝到 targetUser 中
- @Transactional(rollbackFor = Exception.class)
- @Override
- public void updateUser(User user) {
- User targetUser = userRepository.findById(user.getId()).get();
- BeanUtils.copyProperties(user, targetUser);
- userRepository.save(targetUser);
- }
但是,我们通过 Debug 发现,最终 targetUser 对象和 User 对象一模一样,即 targetUser 中原来有值的被覆盖为 null 了。
如果使为 source 对象中为 null 的字段不拷贝呢?
答案是 重写 copyProperties 方法。
- package com.liuyanzhao.forum.util;
- import org.springframework.beans.BeansException;
- import org.springframework.beans.FatalBeanException;
- import org.springframework.util.Assert;
- import java.beans.PropertyDescriptor;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- /**
- * @author 言曌
- * @date 2018/3/25 下午2:27
- */
- public abstract class BeanUtils extends org.springframework.beans.BeanUtils {
- /**
- * 将source中不为空的字段,拷贝(覆盖)到target中
- * @param source
- * @param target
- * @throws BeansException
- */
- public static void copyProperties(Object source, Object target) throws BeansException {
- Assert.notNull(source, "Source must not be null");
- Assert.notNull(target, "Target must not be null");
- Class<?> actualEditable = target.getClass();
- PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
- for (PropertyDescriptor targetPd : targetPds) {
- if (targetPd.getWriteMethod() != null) {
- PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
- if (sourcePd != null && sourcePd.getReadMethod() != null) {
- try {
- Method readMethod = sourcePd.getReadMethod();
- if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
- readMethod.setAccessible(true);
- }
- Object value = readMethod.invoke(source);
- // 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时格式转换等等
- if (value != null) {
- Method writeMethod = targetPd.getWriteMethod();
- if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
- writeMethod.setAccessible(true);
- }
- writeMethod.invoke(target, value);
- }
- } catch (Throwable ex) {
- throw new FatalBeanException("Could not copy properties from source to target", ex);
- }
- }
- }
- }
- }
- }
然后导入我们重写的 BeanUtils 类,使用我们自定义的 copyProperties() 方法,成功解决。
2019年04月25日 11:01:11
BeanUtils第32行,判断sourcePd != null没有必要吧?这个条件应该常为true。