一、理论基础
需要掌握知识点:
- vo bo dto do区别
- 接口细分安全领域dto与do
- dto细拆分输入与输出
- 手写Dto与Do转换工具类
1.1、相关概念
(1)VO(View Object):视图对象。
用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
(2)DTO(Data Transfer Object):数据传输对象。
主要用于外部接口参数传递封装。公开给别人可以看到的一些字段
(3)DO(Domain Object):领域对象。
主要用于操作数据库层传递。就是从现实世界中抽象出来的有形或无形的业务实体。
(4)PO(Persistent Object):持久化对象。
它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。
在日常的项目开发中,VO对应于页面上需要显示的数据(表单),DO对应于数据库中存储的数据(数据表),DTO对应于除二者之外需要进行接口形式传递的数据。
流程如下:
- l 用户发出请求(可能是填写表单),表单的数据在展示层被匹配为VO。
- l 展示层把VO转换为服务层对应方法所要求的DTO,传送给服务层。
- l 服务层首先根据DTO的数据构造(或重建)一个DO,调用DO的业务方法完成具体业务。
- (DTO->DO:接口接收参数,转换为数据库实体类插入数据库中;DO->DTO:向数据库查询数据转换DTO返回给客户端)
- l 服务层把DO转换为持久层对应的PO,调用持久层的持久化方法,把PO传递给它,完成持久化操作。
- l 对于一个逆向操作,如读取数据,也是用类似的方式转换和传递,略。
1.2、VO与DTO的区别
既然DTO是展示层与服务层之间传递数据的对象,为什么还需要一个VO呢?
对于绝大部分的应用场景来说,DTO和VO的属性值基本是一致的,而且他们通常都是POJO,因此没必要多此一举,但不要忘记这是实现层面的思维,对于设计层面来说,概念上还是应该存在VO和DTO,因为两者有着本质的区别:
DTO代表服务层需要接收的数据和返回的数据,而VO代表展示层需要显示的数据
VO与DTO的应用:
在以下才场景中,我们可以考虑把VO与DTO二合为一:
当需求非常清晰稳定,而且客户端很明确只有一个的时候,没有必要把VO和DTO区分开来,这时候VO可以退隐,用一个DTO即可
1.3、DTO与DO的区别
DTO是展示层和服务层之间的数据传输对象,而DO是对现实世界各种业务角色的抽象,这就引出了两者在数据上的区别,例如UserInfo和User。
安全性问题:
对于一个getUser(DTO)方法来说,本质上它永远不应该返回用户的密码,因此UserInfo(DO)至少比User少一个password的数据
通过反射即可转换
DTO与DO的应用:
(1)DO具有一些不应该让展示层知道的数据;
(2)DO具有业务方法,如果直接把DO传递给展示层,展示层的代码就可以绕过服务层直接调用它不应该访问的操作,对于基于AOP拦截服务层来进行访问控制的机制来说,这问题尤为突出,而在展示层调用DO的业务方法也会因为事务的问题,让事务难以控制。
1.4、DO与PO的区别
DO和PO在绝大部分情况下是一一对应的,但是PO是只含有get/set方法的POJO
二、代码实战
2.1、DO与DTO相互转换工具类
public class BeanUtils<Dto, Do> {
/**
* dot 转换为Do 工具类
*
* @param dtoEntity
* @param doEntity
* @return
*/
public static <Do> Do dtoToDo(Object dtoEntity, Class<Do> doClass) {
// 判断dto是否为空!
if (dtoEntity == null) {
return null;
}
// 判断DoClass 是否为空
if (doClass == null) {
return null;
}
try {
Do newInstance = doClass.newInstance();
BeanUtils.copyProperties(dtoEntity, newInstance);
// Dto转换Do
return newInstance;
} catch (Exception e) {
return null;
}
}
/**
* do 转换为Dto 工具类
*
* @param dtoEntity
* @param doEntity
* @return
*/
public static <Dto> Dto doToDto(Object doEntity, Class<Dto> dtoClass) {
// 判断dto是否为空!
if (doEntity == null) {
return null;
}
// 判断DoClass 是否为空
if (dtoClass == null) {
return null;
}
try {
Dto newInstance = dtoClass.newInstance();
BeanUtils.copyProperties(doEntity, newInstance);
// Dto转换Do
return newInstance;
} catch (Exception e) {
return null;
}
}
}