前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >仿BeanUtils反射实现DTO速转VO

仿BeanUtils反射实现DTO速转VO

原创
作者头像
阿新的杂记
发布2023-08-22 23:17:56
2530
发布2023-08-22 23:17:56
举报
文章被收录于专栏:阿新的杂记阿新的杂记

前言

在开发的过程中,我们要常常要实现DTO(数据传输对象)转VO(视图对象)。

可能你会问,什么是DTO,什么是VO?

DTO(Data Transfer Object)数据传输对象 1、在服务间的调用中,传输的数据对象 2、个人理解,DTO是可以存在于各层服务中(接口、服务、数据库等等)服务间的交互使用DTO来解耦

VO (view object/value object)表示层对象 1、前端展示的数据,在接口数据返回给前端的时候需要转成VO 2、个人理解使用场景,接口层服务中,将DTO转成VO,返回给前台

那么,高效的实现DTO(数据传输对象)转VO(视图对象)非常重要。

业务场景举例

我们先来看一个业务场景。

我们在请求登录成功的时候,我们毫无疑问要从数据库获取信息,并且将部分信息返回到前端。

我们从数据库获取信息时的数据传输对象(dto),假设是这样的:

代码语言:javascript
复制
@Data
@TableName("db_account")
@AllArgsConstructor
public class Account implements BaseData {
    @TableId(type = IdType.AUTO)
    Integer id;
    String username;
    String password;
    String email;
    String role;
    @TableField("registerTime")
    Date registerTime;
}

而我们返回给前端的时候,不能返回过多的数据,比如密码这些我们肯定不能返回,我们通常返回部分数据回去。

假设我们使用jwt的校验方案,那么毫无疑问得返回token回去。

下面是我们要返回的表示层对象(vo)

代码语言:javascript
复制
@Data
public class AuthorizeVO {
    String username;
    String role;
    String token;
    Date expire;
}

那么从dto传输值到vo。我们有很多方案。

方案一:直接手动设置

如下,我们可以这样以get和set的方式,进行手动设置。

但是问题也明显,代码冗余,可维护性差、可读性差、增加错误风险!

后期的开发,我们会有很多的dto和vo对象,如果都是这样手动设置,显然非常低效。

代码语言:javascript
复制
vo.setUsername(account.getUsername());
vo.setToken(token);
vo.setRole(account.getRole());
vo.setExpire(utils.expireTime());

方案二:使用官方提供工具库

我们可以使用BeanUtils.copyProperties() 去除重复代码,BeanUtils.copyProperties()底层是使用了反射实现。

但是这样我觉得依然不是很优雅,因为他是对相同的值进行设置,我们还要在后面去设置没有相同字段的值。

代码语言:javascript
复制
BeanUtils.copyProperties(account,vo);

方案三:自己实现dto转vo接口

我们知道,BeanUtils是通过反射机制实现,那我们也可以通过反射实现一个属于自己的dto转vo。

我的代码如下:

通过反射机制实现了将DTO对象转换为VO对象的功能。它可以通过让DTO类实现BaseData接口并调用接口中的方法来实现转换。转换过程中,会将DTO对象中的字段值复制到VO对象中,从而实现数据的传输和转换。

代码语言:javascript
复制
package com.example.entity;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.function.Consumer;

/**
 * 用于DTO快速转换VO实现,只需将DTO类继承此类即可使用
 */
public interface BaseData {

    /**
     * 创建指定的VO类并将当前DTO对象中的所有成员变量值直接复制到VO对象中
     * @param clazz 指定VO类型
     * @param consumer 返回VO对象之前可以使用Lambda进行额外处理
     * @return 指定VO对象
     * @param <V> 指定VO类型
     */
    default <V> V asViewObject(Class<V> clazz, Consumer<V> consumer) {
        V v = this.asViewObject(clazz);
        consumer.accept(v);
        return v;
    }


    /**
     * 创建指定的VO类并将当前DTO对象中的所有成员变量值直接复制到VO对象中
     * @param clazz 指定VO类型
     * @return 指定VO对象
     * @param <V> 指定VO类型
     */
    default <V> V asViewObject(Class<V> clazz) {
        try {
            Field[] fields = clazz.getDeclaredFields();
            Constructor<V> constructor = clazz.getConstructor();
            V v = constructor.newInstance();
            Arrays.asList(fields).forEach(field -> convert(field,v));
            return v;
        } catch (ReflectiveOperationException exception) {
            Logger logger = LoggerFactory.getLogger(BaseData.class);
            logger.error("在VO与DTO转换时出现了一些错误");
            throw new RuntimeException(exception.getMessage());
        }
    }

    /**
     * 内部使用,快速将当前类中目标对象字段同名字段的值复制到目标对象字段上
     * @param field 目标对象字段
     * @param target 目标对象
     */
    private void convert(Field field, Object target) {
        try {
            Field source = this.getClass().getDeclaredField(field.getName());
            field.setAccessible(true);
            source.setAccessible(true);
            field.set(target,source.get(this));
        } catch (IllegalAccessException | NoSuchFieldException ignored) {}
    }
}
  • 接口定义:BaseData是一个接口,用于提供DTO转换为VO的方法。通过让DTO类实现该接口,就可以使用接口中定义的方法进行转换操作。
  • asViewObject方法:这个方法用于创建指定类型的VO对象,并将当前DTO对象中的成员变量值直接复制到VO对象中。它接受一个Class<V>参数,表示指定的VO类型。方法内部使用反射来获取VO类的构造函数,创建一个VO对象,并通过遍历VO类的所有字段,将DTO对象对应字段的值复制到VO对象中。
  • asViewObject方法重载:这个方法是对上述方法的重载,添加了一个额外的Consumer<V>参数。这个参数可以使用Lambda表达式,用于在返回VO对象之前对其进行额外处理。例如,可以在这里对VO对象的某些字段进行修改或设置。
  • convert方法:这是一个私有方法,用于在内部快速将DTO对象字段的值复制到VO对象字段上。它接受一个目标对象字段和目标对象作为参数。方法内部使用反射来获取DTO对象字段的值,并将其设置到目标对象字段上。

使用方法:

代码语言:javascript
复制
AuthorizeVO vo = account.asViewObject(AuthorizeVO.class, v-> {
    v.setExpire(utils.expireTime());
    v.setToken(token);
});

这样,我们不仅可以实现类型BeanUtils的功能,还能利用Lambda,一步到位的实现,减少冗余代码。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 业务场景举例
  • 方案一:直接手动设置
  • 方案二:使用官方提供工具库
  • 方案三:自己实现dto转vo接口
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com