ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

java-design-patterns 实战:Java 数据传输对象(DTO)模式原理与代码实现

java-design-patterns 实战:Java 数据传输对象(DTO)模式原理与代码实现 示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载数据传输对象Data Transfer Object简称 DTO是 java-design-patterns 仓库中一个经典的架构模式Architectural Pattern性能向 Tag核心思想是把多个相互关联的属性打包进一个对象通过单次后端查询或单次远程调用完成传输从而避免为每个属性发起一次远程请求。本文以仓库中的 数据传输对象模式中文文档 为骨架结合data-transfer-object模块的完整源码、类图、时序图与单元测试讲解 DTO 的设计动机、两种典型实现方式传统 POJO 与 enum 分组式 DTO、敏感数据分级隔离的实战用法以及如何在本地运行和测试该示例帮助你直接上手并迁移到自己的分层架构中。模式目的为什么要用 DTO在客户端-服务器或进程间通信场景中每次远程方法调用都是昂贵操作其成本大头来自客户端与服务器之间的往返时间round-trip time而不是调用本身。如果客户端需要获取一个对象的多个属性例如客户的 id、firstName、lastName逐个属性地发起请求就会产生多次网络往返严重拖慢整体性能。DTO 模式的目的是将具有多个属性的数据从客户端传递到服务器或反过来一次传送所有相关属性避免多次调用远程服务器。用一个对象DTO聚合本应由多次调用分别传输的数据最终仅由一次调用完成服务从而在客户端与服务器之间建立一条低成本、高吞吐的数据通道。维基百科对它的定义也印证了这一点DTO 是在进程之间承载数据的对象由于进程间通信通常依赖远程接口如 Web 服务每次调用都很昂贵因此减少调用次数的一种方式就是使用一个聚合了多次调用所需数据的 DTO由一次调用统一提供。通俗地说使用 DTO可以通过单个后端查询获取所有相关信息。适用场景根据仓库文档当出现以下情况时应当使用 DTO 模式客户端请求多种信息且这些信息彼此相关如一个客户的多个字段、一个产品的多个维度想要提高获取资源的性能减少网络传输开销想要降低远程方法调用的次数尤其是在客户端-服务器架构中。此外从英文版 README 的说明可以补充两点适用信号在批量处理数据优于逐条处理的场景以及在与远程接口协作时把要传输的数据封装进一个可序列化的对象以便传输。程序示例Customer DTO传统 POJO 实现文档用客户信息查询作为第一个示例我们需要从远程数据库中获取客户信息不再一次查询一个属性而是用一个 DTO 一次传送所有相关属性。1. 定义 CustomerDto中文文档中给出的CustomerDto是经典 POJO 形式——不可变字段加只读 getterpublic class CustomerDto { private final String id; private final String firstName; private final String lastName; public CustomerDto(String id, String firstName, String lastName) { this.id id; this.firstName firstName; this.lastName lastName; } public String getId() { return id; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } }在仓库的实际源码中CustomerDto.java 已经改用 Java 16 的record写法语义与文档中的类版本完全等价但代码量大幅缩减public record CustomerDto(String id, String firstName, String lastName) {}record自动生成构造器、访问器id()、firstName()、lastName()以及equals/hashCode/toString天然具备不可变性。源码注释还强调了 DTO 的一条重要纪律DTO 不包含任何业务逻辑它只是一个纯粹的数据载体POJO。2. 编写 CustomerResource 服务端CustomerResource.java 在演示中充当服务器持有一份客户 DTO 列表并提供增删查能力public record CustomerResource(ListCustomerDto customers) { public void save(CustomerDto customer) { customers.add(customer); } public void delete(String customerId) { customers.removeIf(customer - customer.id().equals(customerId)); } }3. 客户端一次调用获取全部信息在 App.java 的main方法中客户端App自身扮演只需要调用一次customerResource.customers()即可拿到所有客户信息并逐个打印姓氏而无需为每个字段分别请求var customerOne new CustomerDto(1, Kelly, Brown); var customerTwo new CustomerDto(2, Alfonso, Bass); var customers new ArrayList(List.of(customerOne, customerTwo)); var customerResource new CustomerResource(customers); LOGGER.info(All customers:); var allCustomers customerResource.customers(); printCustomerDetails(allCustomers); // Kelly // Alfonso随后的删除与新增同样只通过 DTO 完成customerResource.delete(customerOne.id())删除客户一customerResource.save(new CustomerDto(3, Lynda, Blair))追加客户三。整个交互过程中客户端与远程服务器之间只发生了 DTO 粒度的调用而不是属性粒度的调用。程序示例Product DTOenum 分组式实现仓库的第二个示例ProductDto.java展示了一种面向 API 的分组式 DTO 设计把请求 DTO 与响应 DTO 按用途分层组织并利用响应 DTO 的字段组合实现敏感数据分级隔离。1. 实体类 ProductProduct.java 是领域实体通过 Lombok 的Data、Builder、NoArgsConstructor、AllArgsConstructor生成样板代码字段包括id、name、price、cost、supplierData Builder NoArgsConstructor AllArgsConstructor public final class Product { private Long id; private String name; private Double price; private Double cost; private String supplier; }注意实体与 DTO 的职责分工实体承载完整领域数据包括机密的cost与supplier而对外暴露什么字段由 DTO 决定。2. 分层的 ProductDtoRequest 与 ResponseProductDto本身是一个空枚举仅作为命名空间内部按用途分为两大组Request 组ProductDto.Request.Create用于客户端发起创建商品请求包含name、price、cost、supplier四个可链式赋值的字段setName(...)返回this支持链式调用。Response 组ProductDto.Response.Public与ProductDto.Response.Private分别面向普通客户与管理后台DTO 类型包含字段数据敏感度适用对象Response.Publicid、name、price低price 非机密普通客户Response.Privateid、name、price、cost高cost 机密、supplier 高度机密管理员源码中通过私有接口Id、Name、Price、Cost、Supplier约定字段语义例如Price接口注释标明这是我们销售产品的价格该数据不机密而Cost与Supplier分别标注该数据机密与该数据高度机密——把安全语义直接写进了类型定义。3. ProductResource按角色返回不同 DTOProductResource.java 根据调用方身份把实体映射为不同字段集合的 DTOpublic ListProductDto.Response.Private getAllProductsForAdmin() { return products.stream() .map(p - new ProductDto.Response.Private() .setId(p.getId()).setName(p.getName()) .setCost(p.getCost()).setPrice(p.getPrice())) .toList(); } public ListProductDto.Response.Public getAllProductsForCustomer() { return products.stream() .map(p - new ProductDto.Response.Public() .setId(p.getId()).setName(p.getName()) .setPrice(p.getPrice())) .toList(); }可以看到管理员视图包含cost成本价客户视图则被剥离掉cost与supplier。这就是 DTO 在 API 设计中最重要的价值之一——在不改动领域实体与底层存储的前提下通过不同 DTO 形态控制信息暴露范围。4. 保存新商品保存动作同样使用请求 DTO 作为入参由ProductResource.save(...)把请求 DTO 转换为实体后写入列表源码中自动分配id products.size() 1ProductDto.Request.Create createProductRequestDto new ProductDto.Request.Create() .setName(PS5).setCost(1000D).setPrice(1220D).setSupplier(Sony); productResource.save(createProductRequestDto);类图与时序图文档中的类图展示了CustomerDto、CustomerResource与客户端应用三类角色的静态关系CustomerResource持有ListCustomerDto客户端调用getAllCustomers()获取 DTO 列表Data Transfer Object 模式类图仓库还提供了一张完整时序图清晰展示了 DTO 在分层架构Client → Service → Repository → Database中的流转过程客户端调用requestData()请求数据Service 通过 Repository 查询数据库拿到实体getEntity()/query()随后调用populateFromEntity(entity)将实体转换为 DTO最后将 DTO 返回给客户端——客户端始终只与 DTO 打交道一次请求即拿到全部数据Data Transfer Object 模式时序图两张图对应的 PlantUML 源文件分别为>./mvnw -pl>赞分享示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载相关推荐java-design-patterns 项目精读Java 抽象工厂Abstract Factory模式原理与实战java design patterns 项目精读Java 抽象工厂Abstract Factory模式原理与实战 本指南以开源仓库 java desig示例工程教程Java 设计模式之 Converter 模式以 java-design-patterns 为例详解 DTO 与领域对象的双向转换Java 设计模式之 Converter 模式以 java design patterns 为例详解 DTO 与领域对象的双向转换 Converter转换器示例工程教程Java Object Pool 对象池模式实战以 java-design-patterns 仓库为例详解可复用对象管理Java Object Pool 对象池模式实战以 java design patterns 仓库为例详解可复用对象管理 导读 对象池Object Pool示例工程教程上一篇告别马赛克与黑边暗黑破坏神2高清补丁D2DX完整上手笔记下一篇xmly-downloader-qt5本地保存攻略4步把喜马拉雅VIP专辑装进硬盘创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表