Java中DTO和VO的区别举例详解

 更新时间:2025年12月25日 10:53:33   作者:趁你还年轻_  
在Java开发中,VO和DTO都是用于数据传输的对象,但它们的含义和用途有所不同,这篇文章主要介绍了Java中DTO和VO区别的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

DTO(Data Transfer Object)和 VO(Value Object / View Object)是 Java 开发中用于不同层次间数据传递的对象,但它们的设计目的和使用场景有本质区别。

一、概念定义

DTO(Data Transfer Object)

  • 定义:用于在不同层或系统之间传输数据的对象

  • 核心作用:减少远程调用次数,打包多个数据一次性传输

  • 典型场景:Service 层返回数据给 Controller 层、微服务间调用

VO(Value Object / View Object)

  • 定义:用于前端展示的数据对象,封装页面需要的数据

  • 核心作用:适配前端展示需求,隐藏后端敏感字段

  • 典型场景:Controller 层返回给前端

二、核心区别对比表

对比维度DTO (Data Transfer Object)VO (View Object)
设计目的减少网络调用,批量传输数据适配前端展示需求
数据范围可能包含多个聚合的数据针对单个页面/组件定制
数据敏感度可包含内部字段必须隐藏敏感字段(密码、金额等)
使用层次Service ↔ Controller、微服务间调用Controller ↔ 前端
字段格式化通常是原始数据类型包含格式化后的数据(如日期、金额显示格式)
变更频率相对稳定随前端需求频繁变化

三、详细说明与代码示例

场景:用户订单详情查询

数据库实体(DO - Data Object)

@Entity
@Table(name = "t_user")
public class UserDO {
    private Long id;
    private String username;
    private String password; // 敏感字段
    private String email;
    private String phone;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    private Integer status; // 0-注销 1-正常 2-冻结
}

DTO(Service 层返回)

@Data
public class UserOrderDTO {
    // 用户基本信息
    private Long userId;
    private String username;
    private String email; // 可能包含敏感信息
    
    // 订单列表(跨聚合数据)
    private List<OrderDTO> orders;
    
    // 统计信息
    private Integer totalOrderCount;
    private BigDecimal totalAmount;
    
    // 原始数据类型
    private LocalDateTime userCreateTime;
}

使用场景:Service 层查询用户和订单数据,打包返回给 Controller

@Service
public class UserOrderService {
    public UserOrderDTO getUserWithOrders(Long userId) {
        UserDO user = userDao.findById(userId);
        List<OrderDO> orders = orderDao.findByUserId(userId);
        
        // 转换并聚合数据
        UserOrderDTO dto = new UserOrderDTO();
        dto.setUserId(user.getId());
        dto.setUsername(user.getUsername());
        dto.setEmail(user.getEmail()); // 直接暴露原始邮箱
        dto.setOrders(convertOrders(orders));
        dto.setTotalOrderCount(orders.size());
        return dto;
    }
}

VO(Controller 层返回前端)

@Data
public class UserOrderVO {
    // 前端展示字段(脱敏)
    private Long userId;
    private String username;
    private String maskedEmail; // 脱敏:a****@gmail.com
    
    // 订单数据(按需展示)
    private List<OrderVO> orders;
    
    // 格式化后的数据
    private String formattedTotalAmount; // "¥9,999.00"
    private String userCreateTimeDesc; // "2025-01-15 12:00"
    
    // 前端状态(计算得出)
    private String userStatusText; // "正常" 而非 1
    
    // 不包含:password, phone, raw status
}

使用场景:Controller 层转换 DTO 为 VO,适配前端展示

@RestController
@RequestMapping("/api/users")
public class UserOrderController {
    
    @Resource
    private UserOrderService userOrderService;
    
    @GetMapping("/{userId}/orders")
    public ResultVO<UserOrderVO> getUserOrders(@PathVariable Long userId) {
        UserOrderDTO dto = userOrderService.getUserWithOrders(userId);
        
        // DTO → VO 转换(关键一步!)
        UserOrderVO vo = new UserOrderVO();
        vo.setUserId(dto.getUserId());
        vo.setUsername(dto.getUsername());
        
        // 1. 脱敏处理
        vo.setMaskedEmail(maskEmail(dto.getEmail())); // a****@gmail.com
        
        // 2. 数据格式化
        vo.setFormattedTotalAmount(formatCurrency(dto.getTotalAmount()));
        vo.setUserCreateTimeDesc(formatDateTime(dto.getUserCreateTime()));
        
        // 3. 状态转换
        vo.setUserStatusText(convertStatusToText(user.getStatus())); // "正常"
        
        // 4. 按需过滤字段
        vo.setOrders(convertToOrderVO(dto.getOrders()));
        
        return ResultVO.success(vo);
    }
}

四、常见误区与反例

❌ 误区 1:DTO 和 VO 混用

// 错误:Controller 直接返回 DTO,暴露敏感信息
@RestController
public class UserController {
    public UserOrderDTO getUserOrders(Long userId) {
        return userOrderService.getUserWithOrders(userId); // ❌ 暴露了 password 等字段
    }
}

❌ 误区 2:VO 包含业务逻辑

// 错误:VO 包含计算逻辑
@Data
public class UserOrderVO {
    private BigDecimal orderAmount;
    private BigDecimal discount;
    
    // ❌ VO 只应承载数据,不应有业务方法
    public BigDecimal getPayAmount() {
        return orderAmount.subtract(discount); // 计算应在后端完成
    }
}

❌ 误区 3:过度使用 DTO 导致类爆炸

// 错误:为每个方法都创建 DTO
public class UserOrderService {
    public UserOrderDTO1 method1() { ... }
    public UserOrderDTO2 method2() { ... }
    public UserOrderDTO3 method3() { ... } // 维护噩梦
}

✅ 正确做法:合理复用 DTO,仅在字段差异大时创建新类

五、与其他对象的关系

在分层架构中,完整的对象体系如下:

完整对象体系

缩写全称作用位置示例
DOData Object持久层 ↔ 数据库UserDO(对应数据库表)
DTOData Transfer ObjectService ↔ ControllerUserOrderDTO(聚合数据)
VOView ObjectController ↔ 前端UserOrderVO(脱敏+格式化)
BOBusiness ObjectService 层内部OrderBO(处理业务逻辑)
POPersistant Object同 DO(老叫法)UserPO
AOApplication Object应用层参数LoginAO(登录参数)

六、最佳实践总结

1. 转换时机明确

// 分层转换
DAO层 → Service层 → Controller层 → 前端
  DO   →   DTO    →    VO      → JSON

2. 命名规范

// DTO 命名:业务 + DTO
UserDTO, OrderDTO, UserOrderDTO
 
// VO 命名:页面/组件 + VO
OrderListVO, OrderDetailVO, UserProfileVO

3. 工具类辅助转换

@Component
public class UserConverter {
    
    // DO → DTO
    public UserDTO toDTO(UserDO userDO) {
        UserDTO dto = new UserDTO();
        BeanUtils.copyProperties(userDO, dto);
        return dto;
    }
    
    // DTO → VO(关键:脱敏+格式化)
    public UserVO toVO(UserDTO userDTO) {
        UserVO vo = new UserVO();
        vo.setUserId(userDTO.getUserId());
        vo.setUsername(userDTO.getUsername());
        vo.setMaskedEmail(this.maskEmail(userDTO.getEmail()));
        vo.setStatusText(this.convertStatus(userDTO.getStatus()));
        return vo;
    }
    
    private String maskEmail(String email) {
        // 脱敏逻辑
        return email.replaceAll("(?<=.).(?=.*@)", "*");
    }
}

4. 统一返回结构

@Data
public class ResultVO<T> {
    private int code;      // 状态码
    private String message; // 提示信息
    private T data;        // 实际数据(VO/DTO)
    
    public static <T> ResultVO<T> success(T data) {
        ResultVO<T> result = new ResultVO<>();
        result.setCode(200);
        result.setMessage("success");
        result.setData(data);
        return result;
    }
}

七、总结

规则DTOVO
核心目标传输效率(减少调用次数)展示安全(适配前端+脱敏)
数据来源可能跨多个聚合通常基于单个 DTO
是否脱敏❌ 否✅ 必须脱敏
是否格式化❌ 原始数据✅ 前端友好格式
位置Service → ControllerController → 前端

设计原则先设计 VO 满足前端需求,再反推 DTO 需要聚合哪些数据,最后确定 DO 的数据库设计。

一句话总结DTO 为服务层效率而生,VO 为前端安全而生

到此这篇关于Java中DTO和VO区别的文章就介绍到这了,更多相关Java DTO和VO区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 一文详解如何在idea中快速搭建一个Spring Boot项目

    一文详解如何在idea中快速搭建一个Spring Boot项目

    IntelliJ IDEA作为Java开发者的‌首选IDE‌,深度集成Spring Boot支持,可一键生成项目骨架、智能配置依赖,这篇文章主要介绍了如何在idea中快速搭建一个Spring Boot项目的相关资料,需要的朋友可以参考下
    2025-06-06
  • Spring AOP定义Before增加实战案例详解

    Spring AOP定义Before增加实战案例详解

    这篇文章主要介绍了Spring AOP定义Before增加,结合实例形式详细分析了Spring面向切面AOP定义Before增加相关定义与使用技巧,需要的朋友可以参考下
    2020-01-01
  • skywalking分布式服务调用链路追踪APM应用监控

    skywalking分布式服务调用链路追踪APM应用监控

    这篇文章主要为大家介绍了skywalking分布式服务调用链路追踪APM应用监控的功能使用说明,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-03-03
  • 一文了解Spring中拦截器的原理与使用

    一文了解Spring中拦截器的原理与使用

    在web开发中,拦截器是经常用到的功能。它可以帮我们预先设置数据以及统计方法的执行效率等等。今天就来详细的谈一下spring中的拦截器,需要的可以参考一下
    2022-06-06
  • Spring Cloud Ribbon的踩坑记录与原理详析

    Spring Cloud Ribbon的踩坑记录与原理详析

    这篇文章主要给大家介绍了关于Spring Cloud Ribbon踩坑记录与原理的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-10-10
  • SpringCloud Alibaba环境集成之nacos详解

    SpringCloud Alibaba环境集成之nacos详解

    Spring Cloud Alibaba提供了越来越完善的各类微服务治理组件,比如分布式服务配置与注册中心nacos,服务限流、熔断组件sentinel等,本篇先来介绍SpringCloud Alibaba环境集成之nacos详解,需要的朋友可以参考下
    2023-03-03
  • Java实现在Word指定位置插入分页符

    Java实现在Word指定位置插入分页符

    在Word插入分页符可以在指定段落后插入,也可以在特定文本位置处插入。本文将以Java代码来操作以上两种文档分页需求,需要的可以参考一下
    2022-04-04
  • 详解什么是Java线程池的拒绝策略?

    详解什么是Java线程池的拒绝策略?

    今天给大家总结一下线程池的拒绝策略,文中有非常详细的介绍及代码示例,对正在学习java的小伙伴们有很好地帮助,需要的朋友可以参考下
    2021-05-05
  • 利用Java实现玩家打怪小游戏的完整过程

    利用Java实现玩家打怪小游戏的完整过程

    这篇文章主要介绍了如何使用Java创建一个简单的“打怪小游戏”,游戏中的角色分为法师、战士、BOSS和一个Team类,代码展示了如何通过面向对象编程来实现一个基本的战斗系统,需要的朋友可以参考下
    2024-12-12
  • Mybatis框架中Interceptor接口的使用说明

    Mybatis框架中Interceptor接口的使用说明

    这篇文章主要介绍了Mybatis框架中Interceptor接口的使用说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09

最新评论