首先明确一个核心概念:POJO(Plain Old Java Object) 本身并不是某一种具体的"类型",而是一种设计理念——指普通的、不继承/实现特定框架类/接口的 Java 对象。在它之上,业界衍生出了各种带有特定职责的"对象类型"。以下是常见的几大类:
一、PO(Persistent Object)— 持久化对象
示例:
// 对应 user 表
public class UserPO {
private Long id;
private String username;
private String password;
private String email;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}使用场景/用途:
作为 MyBatis Mapper / JPA Repository 的返回值和入参
只在持久层内部使用,不应直接暴露给 Service 层以上的调用方
当数据库字段变更时,只需改这一个类
二、DTO(Data Transfer Object)— 数据传输对象
示例 1 — 接口返回 DTO:
// 只返回前端需要的字段,避免暴露敏感数据
public class UserDTO {
private Long id;
private String username;
private String avatar;
private String roleName;
}示例 2 — 查询参数 DTO:
public class UserQueryDTO {
private String keyword;
private Integer pageNo;
private Integer pageSize;
private String sortBy;
}使用场景/用途:
API 接口返回值:用 DTO 组装需要返回给前端的数据,隐藏敏感字段(如 password)
RPC/微服务调用:跨服务传参时用 DTO 定义契约,避免内部 PO 泄漏
批量数据传输:减少网络开销,只传必需字段
三、VO(View Object / Value Object)— 视图对象 / 值对象
实际上 VO 有两种含义,需要区分:
3.1 VO = View Object(视图对象)
示例:
// 前端个人中心页需要的数据,来自多个表
public class UserProfileVO {
private String username;
private String nickname;
private String avatarUrl; // 拼接后的完整 URL
private String levelName; // 来自等级表
private String registerTime; // 已格式化的日期字符串
private List<OrderBriefVO> recentOrders;
}使用场景/用途:
一个页面可能对应一个 VO,包含了该页面需要展示的所有数据
可以对后端数据进行格式化、聚合、裁剪,让前端拿到的数据"开箱即用"
避免前端多次请求或做复杂数据处理
3.2 VO = Value Object(值对象)—— DDD 概念
public class Address {
private final String province;
private final String city;
private final String detail;
// 只提供构造器 + getter,不提供 setter
// 重写 equals() 和 hashCode()
@Override
public boolean equals(Object o) {
// 所有字段都相同才相等
}
}使用场景/用途(DDD 中的 VO):
表示描述性概念,如金额(金额+货币单位)、坐标、电话号码
因为没有 ID,不可变、可共享、可替换
四、BO(Business Object)— 业务对象
示例:
// 订单业务对象,包含订单头和明细
public class OrderBO {
private OrderPO order;
private List<OrderItemPO> items;
private UserPO user;
// 包含业务方法
public BigDecimal calcTotalPrice() {
return items.stream()
.map(OrderItemPO::getSubTotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
public boolean canCancel() {
return order.getStatus() == OrderStatus.PENDING;
}
}使用场景/用途:
当业务逻辑复杂,需要操作多个 PO 时,用 BO 将它们聚合
把"散落在 Service 方法中的业务逻辑"迁移到 BO 的方法中,避免 Service 层膨胀
也可以用在报表、统计等场景,封装计算逻辑
五、AO(Application Object)— 应用对象
使用场景/用途:
在 DDD 架构中,AO 负责编排多个领域服务完成一个完整的用户用例
较少直接使用,更多是一种架构概念
六、对比总览
七、最佳实践建议
分层隔离原则:PO 不暴露给 Controller,DTO 不泄露给 DAO
DO → DTO → VO 转换:使用 MapStruct、BeanUtils 等工具转换,不要手写大量 setter
不要滥用:简单的 CRUD 项目不必每种对象都建,分层过度会增加维护成本;复杂的业务系统(如电商、金融)则建议严格分层
命名规范:UserPO / UserDO / UserDTO / UserVO / UserBO,团队统一风格即可