『十倍程序员』Java五大对象映射框架,总有一款适合你😘

「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战」。

1.前言

Hello 大家好,我是l拉不拉米,今天的『十倍程序员』系列给大家分享Java五大映射框架的介绍以及简单实用,如果您还在用BeanUtils.copyProperties(),请赶快换成映射框架

2.简介

创建由多层组成的大型 Java 应用程序需要使用多个模型,例如持久性模型、域模型或所谓的 DTO。为不同的应用层使用多个模型将需要我们提供一种 bean 之间的映射方式。

手动执行此操作可以快速创建大量样板代码并消耗大量时间。幸运的是,Java 有多种对象映射框架。

在本文中,我们将比较最流行的五款 Java 映射框架的性能。

3.映射框架

3.1. Dozer

Dozer 是一种映射框架,它使用递归将数据从一个对象复制到另一个对象。该框架不仅能够在 bean 之间复制属性,而且还可以在不同类型之间自动转换。

要使用 Dozer 框架,我们需要将此类依赖项添加到我们的项目中:

1
2
3
4
5
xml复制代码<dependency> 
<groupId>com.github.dozermapper</groupId>
<artifactId>dozer-core</artifactId>
<version>6.5.0</version>
</dependency>

3.2. Orika

Orika 是一个 bean 到 bean 的映射框架,它递归地将数据从一个对象复制到另一个对象。

Orika 的一般工作原理类似于推土机。两者之间的主要区别在于 Orika 使用字节码生成。这允许以最小的开销生成更快的映射器。

要使用它,我们需要将此类依赖项添加到我们的项目中:

1
2
3
4
5
xml复制代码<dependency> 
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.5.4</version>
</dependency>

3.3. MapStruct

MapStruct 是一个自动生成 bean 映射器类的代码生成器。

MapStruct 还具有在不同数据类型之间进行转换的能力。

要将 MapStruct 添加到我们的项目中,我们需要包含以下依赖项:

1
2
3
4
5
xml复制代码<dependency> 
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.1.Final</version>
</dependency>

3.4. ModelMapper

ModelMapper 是一个框架,旨在通过根据约定确定对象如何相互映射来简化对象映射。它提供类型安全和重构安全的 API。

要在我们的项目中包含 ModelMapper,我们需要添加以下依赖项:

1
2
3
4
5
xml复制代码<dependency> 
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.8</version>
</dependency>

3.5. JMapper

JMapper 是一个映射框架,旨在提供一个易于使用的、高性能的 Java Bean 之间的映射。

该框架旨在使用注释和关系映射来应用 DRY 原则。

该框架支持不同的配置方式:基于注解基于 XML基于 API

要在我们的项目中包含 JMapper,我们需要添加它的依赖项:

1
2
3
4
5
xml复制代码<dependency> 
<groupId>com.googlecode.jmapper-framework</groupId>
<artifactId>jmapper-core</artifactId>
<version>1.6.1.CR2</version>
</dependency>

4.简单实现

4.1. Orika转换器

Orika 允许完整的 API 实现,这极大地简化了映射器的创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
java复制代码public class OrikaConverter implements Converter{ 
private MapperFacade mapperFacade;

public OrikaConverter() {
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(Order.class, SourceOrder.class).field("orderStatus", "status").byDefault().register();
mapperFacade = mapperFactory.getMapperFacade();
}

@Override
public Order convert(SourceOrder sourceOrder) {
return mapperFacade.map(sourceOrder, Order.class);
}

@Override
public DestinationCode convert(SourceCode sourceCode) {
return mapperFacade.map(sourceCode, DestinationCode.class);
}
}

4.2. Dozer转换器

Dozer 需要 XML 映射文件,包含以下部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
xml复制代码<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
https://dozermapper.github.io/schema/bean-mapping.xsd">

<mapping>
<class-a>com.baeldung.performancetests.model.source.SourceOrder</class-a>
<class-b>com.baeldung.performancetests.model.destination.Order</class-b>
<field>
<a>status</a>
<b>orderStatus</b>
</field>
</mapping>
<mapping>
<class-a>com.baeldung.performancetests.model.source.SourceCode</class-a>
<class-b>com.baeldung.performancetests.model.destination.DestinationCode</class-b>
</mapping>
</mappings>

在定义了 XML 映射之后,我们可以从代码中使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码public class DozerConverter implements Converter { 
private final Mapper mapper;

public DozerConverter() {
this.mapper = DozerBeanMapperBuilder.create().withMappingFiles("dozer-mapping.xml").build();
}

@Override public Order convert(SourceOrder sourceOrder) {
return mapper.map(sourceOrder,Order.class);
}

@Override public DestinationCode convert(SourceCode sourceCode) {
return mapper.map(sourceCode, DestinationCode.class);
}
}

4.3. MapStruct转换器

MapStruct 的定义非常简单,因为它完全基于代码生成:

1
2
3
4
5
6
7
8
9
10
11
java复制代码@Mapper 
public interface MapStructConverter extends Converter {
MapStructConverter MAPPER = Mappers.getMapper(MapStructConverter.class);

@Mapping(source = "status", target = "orderStatus")
@Override
Order convert(SourceOrder sourceOrder);

@Override
DestinationCode convert(SourceCode sourceCode);
}

更多使用案例可以参考我之前写的一篇文章:《优雅的对象转换-MapStruct》

4.4. JMapperConverter转换器

JMapperConverter 需要做更多的工作。实现接口后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
java复制代码public class JMapperConverter implements Converter { 
JMapper realLifeMapper;
JMapper simpleMapper;

public JMapperConverter() {
JMapperAPI api = new JMapperAPI().add(JMapperAPI.mappedClass(Order.class));
realLifeMapper = new JMapper(Order.class, SourceOrder.class, api);
JMapperAPI simpleApi = new JMapperAPI().add(JMapperAPI.mappedClass(DestinationCode.class));
simpleMapper = new JMapper( DestinationCode.class, SourceCode.class, simpleApi);
}

@Override
public Order convert(SourceOrder sourceOrder) {
return (Order) realLifeMapper.getDestination(sourceOrder);
}

@Override
public DestinationCode convert(SourceCode sourceCode) {
return (DestinationCode) simpleMapper.getDestination(sourceCode);
}
}

我们还需要为目标类的每个字段添加@JMap 注释。此外,JMapper 无法自行在枚举类型之间进行转换,它需要我们创建自定义映射函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
java复制代码@JMapConversion(from = "paymentType", to = "paymentType") 
public PaymentType conversion(com.baeldung.performancetests.model.source.PaymentType type) {
PaymentType paymentType = null;
switch(type) {
case CARD:
paymentType = PaymentType.CARD;
break;

case CASH:
paymentType = PaymentType.CASH;
break;

case TRANSFER:
paymentType = PaymentType.TRANSFER;
break;
}
return paymentType;
}

4.5. ModelMapper转换器

ModelMapperConverter 要求我们只提供我们想要映射的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码public class ModelMapperConverter implements Converter { 
private ModelMapper modelMapper;

public ModelMapperConverter() {
modelMapper = new ModelMapper();
}

@Override
public Order convert(SourceOrder sourceOrder) {
return modelMapper.map(sourceOrder, Order.class);
}

@Override
public DestinationCode convert(SourceCode sourceCode) {
return modelMapper.map(sourceCode, DestinationCode.class);
}
}

5.最后

可以看到不管使用哪种映射框架,代码量都比使用BeanUtils.copyProperties()要多,不过性能要比BeanUtils.copyProperties()强很多,BeanUtils.copyProperties()使用反射实现,性能较差。

作者个人比较推荐使用mapStruct,在编译期间生成增强代码,性能较高。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%