最近几天在工作中遇到了一个场景,是将一个对象转换成另外一个结构类似的对象,如下所示:

Class Person {
	private String id;
	private String name;
	private String birthday;
	//getters and setters
}

Class PersonDto {
	private String id;
	private String name;
	private Date birthday;
	//getters and setters
}

比方说上面两个类的对象,想要将Person转换为PersonDto,最直接的写法当然是直接用setter:

Class Mapper {
	public PersonDto map(Persion p) {
		PersonDto pd = new PersonDto();
		pd.setId(p.getId());
		pd.setName(p.getName());
		pd.setBirthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(p.getBirthday));
                return pd;
	}
}

但如果Person对象和PersonDto对象的属性非常多,而且还存在对象嵌套呢?

再用直接赋值的方法,代码会显得非常难看臃肿,非常地不便利,这时我就想是否存在一个工具能做这样的转换,于是google了一会,果然有不少提供了对象Mapping功能的工具,其中一个佼佼者就是本文想跟大家介绍的Orika。

Orika简介

GitHub

官方文档

Orika是基于Apache License 2.0的开源项目,项目目标是为了让开发者们从冗长的对象转换逻辑中脱身,将时间放在编写真正有价值的业务代码上。

Orika会自动收集类的元信息,并且以此来生成mapper,用于之后的对象转换和复制,并且是支持递归转换的。

引入

POM引入

<dependency>
   <groupId>ma.glasnost.orika</groupId>
   <artifactId>orika-core</artifactId>
   <version>1.4.2</version><!-- or latest version -->
</dependency>

如果不用maven的话,可以下载jar包,但是还要自行引入依赖javassist/slf4j/paranamer,版本要求见官方文档。

基本用法

//创建mapper工厂
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

//注册mapper
mapperFactory.classMap(PersonSource.class, PersonDestination.class)
   .field("firstName", "givenName")
   .field("lastName", "sirName")
   .byDefault()
   .register();

//获取mapper门面
MapperFacade mapper = mapperFactory.getMapperFacade();
//创建PersonSource对象
PersonSource source = new PersonSource();
// 给source对象设置一些字段
...
// 将PersonSource对象转换为PersonDest
PersonDest destination = mapper.map(source, PersonDest.class);

主要看注册mapper的部分:

  • classMap方法注册了类的映射关系。
  • field方法可以将源对象的字段映射到目标对象的字段,也就是将PersonSource对象映射到PersonDestination对象的时候,PersonSource.firstName会复制到PersonDestination.givenName,lastName同理映射到sirName。
  • byDefault方法则是告诉mapper使用默认mapping策略,基本上来说就是mapper会找同名同类型的字段进行映射。
  • register方法用于完成mapper的注册。

在完成mapper的注册以后,只要用工厂获取mapper门面,然后用map方法就可以完成转换了,省去了自己new对象以及逐个字段赋值的大段代码。

另外如果需要映射的类只有一对,即mapper只用来将PersonSource映射到PersonDestination,而不会再配置一个映射是从PersonSource到PersonDestination2的话,那么更好的实践是采用BoundMapperFacade,即约束mapper,具体代码如下

BoundMapperFacade<PersonSource, PersonDest> boundMapper = mapperFactory.getMapperFacade(PersonSource.class, PersonDest.class);
 
PersonSource source = new PersonSource();
// 为PersonSource对象设置一些字段
...
PersonDest destination = boundMapper.map(source);
//反向映射
PersonSource source2 = boundMapper.mapReverse(destination);

BoundMapper对比标准Mapper提供了更好的性能,使用BoundMapper时如果希望反向映射的话,可以使用mapReverse方法。

OK,以上就是Orika的基本介绍了,当然这还只是皮毛,Orika还提供了更多的高级特性,比方说可以自定义mapper的转换逻辑的custom converters,自定义对象工厂的object factories,自定义mapper,映射过滤器filters等等,基本上能满足绝大部分对象转换的场景,在官方文档上有更加详细的介绍。