一、解整体架构
在典型的 Spring Boot 项目中,一般会分层:
Controller 层 (接口层)
│
▼
Service 层 (业务层)
│
▼
DAO / Repository 层 (数据访问层)
│
▼
Database (数据库)
不同层会使用不同的对象:
Controller
│
├── DTO (接收/返回接口数据)
│
Service
│
├── BO (业务对象)
│
DAO
│
├── PO (数据库映射对象)
│
其他
│
├── VO (展示对象)
└── POJO (普通Java对象)
简单记忆:
| 对象 | 作用 |
|---|---|
| PO | 数据库对象 |
| DTO | 传输对象 |
| VO | 展示对象 |
| BO | 业务对象 |
| DAO | 数据访问对象 |
| POJO | 普通Java对象 |
二、PO(Persistent Object)
PO = Persistent Object(持久化对象)
作用:
数据库表 → Java对象
通常用于 ORM 映射(如 MyBatis / JPA)。
特点:
- 字段与数据库字段 一一对应
- 一般只包含 属性 + getter/setter
- 不包含业务逻辑
示例
数据库表:
user
---------
id
name
age
create_time
PO:
public class UserPO {
private Long id;
private String name;
private Integer age;
private LocalDateTime createTime;
// getter/setter
}
在 DAO 中使用:
@Mapper
public interface UserMapper {
UserPO selectById(Long id);
void insert(UserPO user);
}
总结:
PO = 数据库表结构映射对象
三、DTO(Data Transfer Object)
DTO = 数据传输对象
主要用于:
服务之间传输数据
或者
Controller 接收/返回数据
为什么需要 DTO?
因为:数据库对象不能直接暴露给外部
例如:
UserPO
id
name
password
createTime
但是接口不能返回 password。
所以需要 DTO。
示例:
public class UserDTO {
private Long id;
private String name;
}
Controller:
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/{id}")
public UserDTO getUser(@PathVariable Long id){
return userService.getUser(id);
}
}
Service:
public UserDTO getUser(Long id){
UserPO user = userMapper.selectById(id);
UserDTO dto = new UserDTO();
dto.setId(user.getId());
dto.setName(user.getName());
return dto;
}
总结:
DTO = 用于接口 / RPC / 服务之间传输数据
四、VO(View Object)
VO = View Object(视图对象)
作用:
专门用于返回给前端
VO 和 DTO 很多人混用,但严格来说:
| 对象 | 作用 |
|---|---|
| DTO | 传输 |
| VO | 前端展示 |
VO 更偏向:
UI 展示数据
例如:
用户列表接口
前端需要:
id
name
age
订单数量
VIP等级
这些可能来自多个表。
VO 示例:
public class UserVO {
private Long id;
private String name;
private Integer age;
private Integer orderCount;
private String vipLevel;
}
Controller:
@GetMapping("/list")
public List<UserVO> list(){
return userService.listUsers();
}
总结:
VO = 给前端展示的数据结构
五、BO(Business Object)
BO = Business Object(业务对象)
作用:
封装业务逻辑
BO 通常存在于:
Service 层
例如:
用户注册业务:
校验
创建用户
创建钱包
发送消息
BO:
public class UserBO {
private String name;
private String password;
private String phone;
}
Service:
public void register(UserBO userBO){
// 业务校验
if(userBO.getPassword().length() < 6){
throw new RuntimeException("密码太短");
}
UserPO user = new UserPO();
user.setName(userBO.getName());
user.setPassword(userBO.getPassword());
userMapper.insert(user);
}
总结:
BO = 业务逻辑处理对象
六、DAO(Data Access Object)
DAO = 数据访问对象
作用:
负责操作数据库
DAO 层:
SQL
CRUD
数据库访问
示例:
public interface UserDAO {
UserPO selectById(Long id);
void insert(UserPO user);
void update(UserPO user);
void delete(Long id);
}
在 MyBatis 中:
DAO = Mapper
例如:
@Mapper
public interface UserMapper {
}
总结:
DAO = 数据库访问层
七、POJO(Plain Old Java Object)
POJO = 普通 Java 对象
含义:
没有任何框架依赖的普通Java类
例如:
public class User {
private String name;
private Integer age;
}
特点:
- 不继承框架类
- 不实现特殊接口
- 普通 Java 类
实际上:
PO
DTO
VO
BO
本质上都是 POJO。
关系:
POJO
├── PO
├── DTO
├── VO
└── BO
八、完整流程示例
假设:
查询用户接口
流程:
前端请求
│
▼
Controller
│ DTO
▼
Service
│ BO
▼
DAO
│ PO
▼
Database
代码流程:
1 Controller
@GetMapping("/{id}")
public UserVO getUser(@PathVariable Long id){
UserBO userBO = userService.getUser(id);
UserVO vo = new UserVO();
vo.setName(userBO.getName());
return vo;
}
2 Service
public UserBO getUser(Long id){
UserPO user = userMapper.selectById(id);
UserBO bo = new UserBO();
bo.setName(user.getName());
return bo;
}
3 DAO
UserPO selectById(Long id);
九、真实企业项目结构
project
├── controller
│ └── UserController
│
├── service
│ └── UserService
│
├── dao
│ └── UserMapper
│
├── entity
│ └── UserPO
│
├── dto
│ └── UserDTO
│
├── vo
│ └── UserVO
│
└── bo
└── UserBO
十、很多公司其实是这样用的(真实情况)
实际上很多公司会简化:
PO = Entity
DTO = Request / Response
VO = Response
甚至很多项目只有:
Entity
DTO
或者:
Entity
VO
因为对象太多会导致:
代码膨胀
所以需要根据项目复杂度决定。
十一、总结
一张图记住:
数据库
│
▼
PO (数据库对象)
│
DAO (数据访问)
│
BO (业务处理)
│
DTO (数据传输)
│
VO (前端展示)
口诀:
PO → 数据库
DAO → 数据访问
BO → 业务处理
DTO → 数据传输
VO → 页面展示
POJO → 普通Java对象