首先,为什么要进行数据转换?
在一个分层的体系结构中,经常会使用DTO、PO、VO等封装数据,封装数据到特定的数据对象中,然而在很多情况下,某层内部的数据是不允许传递到其它层,不允许对外暴露的,特别是在分布式的系统中,内部服务的数据对外暴露,也不允许不相关的数据传入到本服务,所以需要对数据对象进行转换。
其次,为什么要使用Dozer?
前期对于很多程序员来说,数据转换都是通过手工编写转换工具类或工具方法来实现的,这样不仅没有针对性而且工作量很大,编写工具类重用性差,而且不灵活。所以,急需要使用一个通用的映射工具,通过配置或少量的编码就可以轻松的实现数据对象之间的转换,Dozer就是这样的映射工具,它具有通用性,灵活性,可重用性和可配置等特点,并且是开源的。
开始使用Dozer,下载Dozer发布包,将dozer.jar添加到你的classpath下,同时需要添加几个dozer运行时的依赖包(google一下)到你的classpath下。当然,如果你的项目使用Maven,只需要将下面的依赖配置粘贴到你的项目中即可,
<dependency> <groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.5.1</version>
</dependency>
如果你的项目中使用的是gradle,只需要将下面的依赖配置粘贴到gradle的配置文件中即可,
compile "net.sf.dozer:dozer:5.5.1"
现有一个UserDTO、一个UserVO,需要将DTO中的数据转换到VO中,具体的代码如下:
public class UserVO {
private long customerId;
private String customerName;
private int customerAge;
private String mobileNo;
private Date createdAt;
//setter、getter此处省略
}
public class UserDTO {
private long userId;
private String userName;
private int userAge;
private String mobileNo;
private String createdAt;
//setter、getter此处省略
}
主要示例代码如下:
public class DozerDemo {
@Test
public void test() {
Mapper mapper = new DozerBeanMapper(new ArrayList<String>() {{
add("dozerBeanMapping.xml");
}});
UserDTO userDTO = new UserDTO();
userDTO.setUserId(12l);
userDTO.setUserName("Dozer");
userDTO.setUserAge(100);
userDTO.setMobileNo("18912345678");
userDTO.setCreatedAt("08/01/2016 17:50:50");
UserVO userVO = mapper.map(userDTO, UserVO.class);
System.out.println("customerId:" + userVO.getCustomerId() + "\ncustomerName:" + userVO.getCustomerName() + "\n" + "customerAge:" + userVO.getCustomerAge() + "\nmobileNo:" + userVO.getMobileNo());
System.out.println("createdAt:" + userVO.getCreatedAt());
}
}
输出结果如下:
customerId:12
customerName:Dozer
customerAge:100
mobileNo:***1234****
createdAt:Mon Aug 01 17:50:50 CST 2016
本示例代码中需要新建一个dozerBeanMapping.xml文件,并将该文件放入src下,示例配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net
http://dozer.sourceforge.net/schema/beanmapping.xsd">
<configuration>
<stop-on-errors>true</stop-on-errors>
<!--<date-format>MM/dd/yyyy HH:mm:ss</date-format>-->
<wildcard>true</wildcard>
</configuration>
<mapping>
<class-a>org.test.UserDTO</class-a>
<class-b>org.test.UserVO</class-b>
<field>
<a>userId</a>
<b>customerId</b>
</field>
<field>
<a>userName</a>
<b>customerName</b>
</field>
<field>
<a>userAge</a>
<b>customerAge</b>
</field>
<field>
<a>mobileNo</a>
<b set-method="setMobileNoWithMask">mobileNo</b>
</field>
<field>
<a date-format="MM/dd/yyyy HH:mm:ss">createdAt</a>
<b>createdAt</b>
</field>
</mapping>
</mappings>
重要提示,如果DTO和VO中的属性名称相同,并且不需要做特殊的数据映射,则不需要dozerBeanMapping.xml,dozer的执行引擎会自动(如果配置<wildcard>true</wildcard>,默认是true)匹配映射相同名称的属性,且只需要如下声明
Mapper mapper = new DozerBeanMapper();
即可。同时,需要注意的是,在实际应用中不建议每次都创建一个新Mapper的实例,一个系统只需要有一个DozerBeanMapper实例。如果结合Spring的DI,只需要添加如下配置即可,
<bean id="dozerMapper" class="org.dozer.DozerBeanMapper">
<property name="mappingFiles">
<list>
<value>classpath:dozerBeanMapping.xml</value>
</list>
</property>
</bean>
从上面创建对象可以看出,DozerBeanMapper的构造函数接收一个List类型的映射配置集合,也就是可以有多个映射文件。
dozerBeanMapping.xml中配置了class-a和class-b分别用于配置两个要互相转换的类(需要加包名),field标签用于配置两个类的属性,a代表class-a类的属性,b代表class-b类的属性,<a>与</a>中是属性名称,只要配置好a和b之后,dozer会将a的值转换给b,Dozer支持的转换类型如下:
Primitive表示基本类型,Wrapper表示包装类型,Complex Type表示复杂类型。Dozer还支持其它类型之间的相互转换,具体请参考Dozer官网:http://dozer.sourceforge.net/
上面的示例代码中,mobileNo转换后,前三和后四都使用了*进行了脱敏,是因为配置了set-method,该配置可以指定转换的方法,这里指定的方法是setMobileNoWithMask,该方法如下:
public void setMobileNoWithMask(String mobileNo) {
this.mobileNo = "***" + mobileNo.substring(3, 7) + "****";
}
上面的createdAt转换后有String类型变成了一个日期类型,是因为配置了date-format,使用指定的日期格式(MM/dd/yyyy HH:mm:ss)进行了格式化。
Dozer可以指定是否单向转换、是否排除某些属性不转换、递归转换等,Dozer的强大远不止这些,在真正需要特殊处理的时候,可以查看官方的文档进行配置即可。