DTO 的关键在于用专属结构和契约主动控制数据流向,而非简单封字段;每个接口配独立 DTO,通过构造函数、注解(如 @JsonProperty(access = WRITE_ONLY))和不可变设计保障边界与一致性。

Java 框架开发中,封装 DTO 的关键不是“封住字段”,而是用结构和契约主动控制数据流向。它要解决的不是“能不能改”,而是“谁在什么场景下能拿到什么、怎么拿、拿完还能不能反向影响内部模型”。
每个接口配一个专属 DTO,不复用、不泛化
不要定义 UserDTO 通用类,再根据场景加注解或条件判断来筛选字段。这会让 DTO 丧失边界感,最终变成实体类的影子。
- 用户列表页 → UserSummaryDTO(只含 id、name、avatar、status)
- 用户详情页 → UserProfileDTO(含 contact、address、joinedAt,不含 password、internalCode)
- 创建订单 → OrderCreateRequestDTO(带校验逻辑的必填字段)
- 查询结果 → OrderListResponseDTO(含分页信息、状态枚举描述)
字段暴露从类定义开始约束,不用运行时过滤
敏感字段的隐藏,不能靠 Controller 层加 @JsonIgnore、@JsonView 或拦截器动态处理。这些方式容易漏、难维护、调用方也看不懂。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 在 DTO 字段上直接用 @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 标记 password、idCard、tenantId 等只允许入参、禁止出参的字段
- 嵌套 DTO(如 AddressDTO 嵌在 UserProfileDTO 中)也要单独标注,不能因为外层安全就默认内层安全
- 避免使用 @JsonIgnore:它不分读写场景,会同时禁掉请求体解析和响应体序列化
用构造函数或 Builder 封装初始化逻辑,拒绝无约束 setter
DTO 不是数据筐,而是有状态契约的轻量对象。一旦允许随意 set,就等于放弃对数据一致性的第一道把关。
- 提供全参构造函数,关键字段(如 orderId、userId)强制传入,不接受 null 或空字符串
- 用 Lombok 的 @Builder(builderMethodName = "create") 配合 @NonNull,让构建过程自带非空校验
- 布尔型字段用 verified 替代 isVerified,构造时传 verified = true,不暴露 setVerified() 方法
- 禁止 public 无参构造 + 所有字段开放 setter 的写法——那是裸数据,不是封装
版本升级靠新 DTO,不改老字段语义
API 版本迭代时,不要给 UserDTO 加个 lastLoginAt 字段,也不要让 status 字段从 String 改成枚举。这会破坏下游兼容性,且掩盖真实变更意图。
- v2 接口返回 UserV2DTO,可继承 UserV1DTO 并新增字段,但 UserV1DTO 保持冻结
- 字段删除 ≠ 字段废弃:已废弃字段保留在老 DTO 中,仅文档标注 deprecated;新 DTO 中不出现
- 字段语义变更(如 amount 含税/不含税切换)必须走新 DTO,不能靠配置开关或上下文判断
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










