Dozer 介绍

dozer 是什么?

说简单点,它就是个高级的 BeanUtils 。它可以实现 一个 java bean 到 另一个 java bean 的数据拷贝和类型转换等功能。

dozer 使用场景

在Java项目中,通常都会定义数据库模型,例如 xxxModel,也有叫 xxxEntity的,这些模型和数据库的表一一对应,并且模型之间还有关系,查询一条数据的时候,会把与之关联的对象也查询出来,这会导致一些问题,例如,有些敏感字段的数据,在某些操作时不可以返回;需要返回json格式的数据格式,在转换json的时候无限递归了。

总之,对应数据库的表有一些JavaBean,对应前端数据展示也有一些JavaBean,通常称之为DTO,VO之类的。

有些人可能会写大量的 set get来解决,也有人可能会用 commons-lang 或者 spring 的 BeanUtils工具类来拷贝,但项目如果比较大,并且数据结构比较复杂,就有必要设计一下了。之前在做SAP的Hybris 的时候里面设计的就很好。

Dozer 就是为了解决这些问题而存在的。

dozer 示例

BeanUtils 功能是基于属性名映射的,同名拷贝, 这个 dozer 默认支持,此外它还支持3种方式:基于XML映射,基于注解映射,基于API映射:下面会简单介绍一下这些方式以及示例代码。

为了展示定义了4个bean,源码在:https://github.com/ckwen/school1024-java 里面

Maven依赖:

<dependency>
<groupId>com.github.dozermapper</groupId>
<artifactId>dozer-core</artifactId>
<version>6.1.0</version>
</dependency>

这里有故事,会在另一篇dozer版本里面介绍。

根据属性名,同名拷贝

UserModel.java

public class UserModel {

private String userId;

private String password;

private String nickname;

public UserModel() {
super();
}

public UserModel(String userId, String password, String nickname) {
super();
this.userId = userId;
this.password = password;
this.nickname = nickname;
}

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getNickname() {
return nickname;
}

public void setNickname(String nickname) {
this.nickname = nickname;
}

@Override
public String toString() {
return "UserModel [userId=" + userId + ", password=" + password + ", nickname=" + nickname + "]";
}

}

UserData.java 和上面的UserModel.java相比,密码属性名字不一样。

public class UserData {

private String userId;

private String nickname;

private String passwd;

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public String getNickname() {
return nickname;
}

public void setNickname(String nickname) {
this.nickname = nickname;
}

public String getPasswd() {
return passwd;
}

public void setPasswd(String passwd) {
this.passwd = passwd;
}

@Override
public String toString() {
return "UserData [userId=" + userId + ", nickname=" + nickname + ", passwd=" + passwd + "]";
}

}

测试用例:

import org.dozer.DozerBeanMapperBuilder;
import org.dozer.Mapper;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.school1024.spring.boot.other.common.dto.UserData;
import com.school1024.spring.boot.other.common.model.UserModel;

public class DozerTest {

private Mapper mapper;

private UserModel model;

@Before
public void before() {
mapper = DozerBeanMapperBuilder.buildDefault();

model = new UserModel("admin", "123456", "Admin");
System.out.println(model);
}

@Test
public void test1() {

UserData data = mapper.map(model, UserData.class);

Assert.assertNotNull(data);

System.out.println(data);

Assert.assertNotNull(data.getUserId());
Assert.assertNotNull(data.getNickname());
Assert.assertTrue(data.getPasswd() == null);
}

@Test
public void test2() {

UserData data = new UserData();

data = mapper.map(model, UserData.class);

Assert.assertNotNull(data);

System.out.println(data);

}
}

输出结果:

UserModel [userId=admin, password=123456, nickname=Admin]
UserData [userId=admin, nickname=Admin, passwd=null]

同名的属性都拷贝了,密码属性名字不一样,导致没有赋值。

基于XML映射

为了能让名字不一样的属性也能拷贝,我们创建了一个xml配置文件:dozer-bean-mappings.xml,文件放在了resources下的dozer文件夹里。

dozer-bean-mappings.xml

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping http://dozermapper.github.io/schema/bean-mapping.xsd">

<mapping>
<class-a>com.school1024.spring.boot.other.common.model.UserModel</class-a>
<class-b>com.school1024.spring.boot.other.common.dto.UserData</class-b>
<field>
<a>password</a>
<b>passwd</b>
</field>
</mapping>

</mappings>

测试类:

import java.util.ArrayList;
import java.util.List;

import org.dozer.DozerBeanMapper;
import org.dozer.DozerBeanMapperBuilder;
import org.dozer.Mapper;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.school1024.spring.boot.other.common.dto.UserData;
import com.school1024.spring.boot.other.common.model.UserModel;

public class MappingsViaXMLTest {

private UserModel model;

@Before
public void before() {

model = new UserModel("admin", "123456", "Admin");
System.out.println(model);
}

@Test
public void testNew() {
DozerBeanMapperBuilder builder = DozerBeanMapperBuilder.create()
.withMappingFiles("dozer/dozer-bean-mappings.xml");

Mapper mapper = builder.build();

UserData data = mapper.map(model, UserData.class);

Assert.assertNotNull(data);

System.out.println(data);

Assert.assertNotNull(data.getUserId());
Assert.assertNotNull(data.getNickname());
Assert.assertTrue(data.getPasswd() == model.getPassword());
}

@Test
public void testOld() {

List<String> mappingXMLList = new ArrayList<>();
mappingXMLList.add("dozer/dozer-bean-mappings.xml");

@SuppressWarnings("deprecation")
Mapper mapper = new DozerBeanMapper(mappingXMLList);

UserData data = mapper.map(model, UserData.class);

Assert.assertNotNull(data);

System.out.println(data);

Assert.assertNotNull(data.getUserId());
Assert.assertNotNull(data.getNickname());
Assert.assertTrue(data.getPasswd() == model.getPassword());
}
}

输出结果:

UserModel [userId=admin, password=123456, nickname=Admin]
UserData [userId=admin, nickname=Admin, passwd=123456]

基于API映射

基于API映射,需要继承BeanMappingBuilder实现一个抽象方法,代码如下:

import org.dozer.loader.api.BeanMappingBuilder;
import org.dozer.loader.api.FieldsMappingOptions;

import com.school1024.spring.boot.other.common.dto.UserData;
import com.school1024.spring.boot.other.common.model.UserModel;

public class UserBeanMappingBuilder extends BeanMappingBuilder {

@Override
protected void configure() {

mapping(UserModel.class, UserData.class).fields("password", "passwd", FieldsMappingOptions.oneWay());

}

}

测试类:

import org.dozer.DozerBeanMapperBuilder;
import org.dozer.Mapper;
import org.junit.Before;
import org.junit.Test;

import com.school1024.spring.boot.other.common.dto.UserData;
import com.school1024.spring.boot.other.common.model.UserModel;

public class MappingsViaAPITest {

private UserModel model;

@Before
public void before() {

model = new UserModel("admin", "123456", "Admin");
System.out.println(model);
}

@Test
public void test() {

DozerBeanMapperBuilder builder = DozerBeanMapperBuilder.create()
.withMappingBuilder(new UserBeanMappingBuilder());

Mapper mapper = builder.build();

UserData data = mapper.map(model, UserData.class);

System.out.println(data);

}
}

输出结果:

UserModel [userId=admin, password=123456, nickname=Admin]
UserData [userId=admin, nickname=Admin, passwd=123456]

基于注解映射

为了让上面2个测试不受注解的影响,因此,重新定义了2个bean。

import org.dozer.Mapping;

public class SourceBean {

private Long id;

// 同名属性默认拷贝
private String name;

@Mapping("binaryData")
private String data;

public SourceBean() {
super();
}

public SourceBean(Long id, String name, String data) {
super();
this.id = id;
this.name = name;
this.data = data;
}

@Mapping("pk")
public Long getId() {
return this.id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

public String getData() {
return data;
}

public void setData(String data) {
this.data = data;
}

@Override
public String toString() {
return "SourceBean [id=" + id + ", name=" + name + ", data=" + data + "]";
}

}
public class TargetBean {

private String pk;

private String name;

private String binaryData;

public String getPk() {
return pk;
}

public void setPk(String pk) {
this.pk = pk;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getBinaryData() {
return binaryData;
}

public void setBinaryData(String binaryData) {
this.binaryData = binaryData;
}

@Override
public String toString() {
return "TargetBean [pk=" + pk + ", name=" + name + ", binaryData=" + binaryData + "]";
}

}

测试类:

import org.dozer.DozerBeanMapperBuilder;
import org.dozer.Mapper;
import org.junit.Test;

public class MappingsViaAnnotationsTest {

@Test
public void test() {

Mapper mapper = DozerBeanMapperBuilder.buildDefault();

SourceBean source = new SourceBean(1L, "Dozer", "2017-08-10");
System.out.println(source);

TargetBean target = mapper.map(source, TargetBean.class);
System.out.println(target);

}
}

输出结果:

SourceBean [id=1, name=Dozer, data=2017-08-10]
TargetBean [pk=1, name=Dozer, binaryData=2017-08-10]
分享到