1. 引言
在构建分层架构的大型 Java 应用时,通常会涉及多种模型:持久层实体、领域模型、DTO 等。不同层级之间频繁的数据传输,必然带来对象之间的映射需求。
如果手动实现 set/get
拷贝,不仅代码冗余、开发效率低,后期维护也是一场灾难。好在生态中有不少成熟的对象映射框架,能帮我们自动完成这项工作。
本文将从性能角度,横向对比目前最主流的 5 款 Java Bean 映射框架,帮你避开踩坑,选型不纠结。
2. 主流映射框架概览
2.1. Dozer
✅ 特点:老牌框架,基于递归实现对象拷贝,支持自动类型转换(如 String ↔ Integer
)。
⚠️ 缺点:运行时通过反射操作,性能较差,且配置依赖 XML。
Maven 依赖:
<dependency>
<groupId>com.github.dozermapper</groupId>
<artifactId>dozer-core</artifactId>
<version>6.5.2</version>
</dependency>
更多用法参考:Dozer 官方文档
2.2. Orika
✅ 特点:与 Dozer 类似,但底层使用 字节码生成技术(如 ASM),生成真正的 Mapper 类,性能更高。
⚠️ 重大限制:当前最新版 1.5.4
在 Java 16+ 环境下会触发 illegal reflective access
错误,无法使用。社区预计 1.6.0
版本修复。
Maven 依赖:
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.5.4</version>
</dependency>
官方文档:Orika 官网
2.3. MapStruct
✅ 特点:编译期代码生成器,在编译时自动生成实现类,无运行时反射开销,性能极佳,且类型安全。
✅ 优势:IDE 友好,支持 Lombok,报错直接定位到注解位置,调试方便。
Maven 依赖:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.6.0.Beta1</version>
</dependency>
官方文档:mapstruct.org
2.4. ModelMapper
✅ 特点:基于“约定优于配置”理念,自动推断字段映射关系,API 简洁。
⚠️ 缺点:完全依赖运行时反射,性能偏低,复杂映射需手动配置。
Maven 依赖:
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.2.0</version>
</dependency>
官方文档:modelmapper.org
2.5. JMapper
✅ 特点:支持注解、XML、API 三种配置方式,强调 DRY 原则,性能表现亮眼。
⚠️ 注意:枚举类型不能自动转换,需手动编写 @JMapConversion
方法。
Maven 依赖:
<dependency>
<groupId>com.googlecode.jmapper-framework</groupId>
<artifactId>jmapper-core</artifactId>
<version>1.6.1.CR2</version>
</dependency>
官方文档:GitHub Wiki
3. 测试模型设计
为了全面评估性能,我们设计了两组测试用例:
3.1. 简单模型(Simple Model)
仅包含单一字段,用于测试基础映射性能。
public class SourceCode {
String code;
// getter and setter
}
public class DestinationCode {
String code;
// getter and setter
}
3.2. 真实业务模型(Real-Life Model)
模拟订单场景,包含嵌套对象、集合、枚举等复杂结构。
public class SourceOrder {
private String orderFinishDate;
private PaymentType paymentType;
private Discount discount;
private DeliveryData deliveryData;
private User orderingUser;
private List<Product> orderedProducts;
private Shop offeringShop;
private int orderId;
private OrderStatus status;
private LocalDate orderDate;
// standard getters and setters
}
public class Order {
private User orderingUser;
private List<Product> orderedProducts;
private OrderStatus orderStatus;
private LocalDate orderDate;
private LocalDate orderFinishDate;
private PaymentType paymentType;
private Discount discount;
private int shopId;
private DeliveryData deliveryData;
private Shop offeringShop;
// standard getters and setters
}
完整模型代码见 GitHub:baeldung-tutorials/model
4. 统一转换器接口
为统一测试入口,定义通用 Converter
接口:
public interface Converter {
Order convert(SourceOrder sourceOrder);
DestinationCode convert(SourceCode sourceCode);
}
各框架实现如下:
4.1. OrikaConverter
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. DozerConverter
需配合 dozer-mapping.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>
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. MapStructConverter
@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);
}
4.4. JMapperConverter
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);
}
}
⚠️ 枚举转换需额外方法:
@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. ModelMapperConverter
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. 简单模型性能测试
使用 JMH(Java Microbenchmark Harness)进行基准测试,涵盖四种模式。
5.1. 平均执行时间(AverageTime)✅ 越小越好
框架 | 平均耗时(ms/操作) |
---|---|
MapStruct | ~0.00001 |
JMapper | ~0.00001 |
Orika | 0.001 |
ModelMapper | 0.002 |
Dozer | 0.004 |
✅ 结论:MapStruct 与 JMapper 并列第一,性能碾压其他框架。
5.2. 吞吐量(Throughput)✅ 越大越好
框架 | 吞吐量(操作/ms) |
---|---|
MapStruct | 58,101 |
JMapper | 53,667 |
Orika | 1,195 |
ModelMapper | 379 |
Dozer | 230 |
✅ MapStruct 略胜一筹,JMapper 紧随其后。
5.3. 单次执行时间(SingleShotTime)✅ 越小越好
框架 | 单次耗时(ms/操作) |
---|---|
JMapper | 0.016 |
MapStruct | 1.904 |
Dozer | 3.864 |
Orika | 6.593 |
ModelMapper | 8.788 |
✅ JMapper 表现最佳,MapStruct 因编译期生成类,首次调用略慢。
5.4. 采样时间(SampleTime)✅ 越低越好
框架 | p0.90 | p0.999 | p1.0 |
---|---|---|---|
JMapper | 0.0001 | 0.001 | 1.526 |
MapStruct | 0.0001 | 0.0001 | 1.948 |
Orika | 0.001 | 0.018 | 2.327 |
ModelMapper | 0.002 | 0.044 | 3.604 |
Dozer | 0.003 | 0.088 | 5.382 |
✅ JMapper 在极端情况下更稳定,MapStruct 次之。
6. 真实业务模型性能测试
6.1. 平均执行时间(AverageTime)
框架 | 平均耗时(ms/操作) |
---|---|
MapStruct | ~0.0001 |
JMapper | ~0.0001 |
Orika | 0.007 |
ModelMapper | 0.137 |
Dozer | 0.145 |
✅ MapStruct 与 JMapper 依然领跑。
6.2. 吞吐量(Throughput)
框架 | 吞吐量(操作/ms) |
---|---|
MapStruct | 3,467 |
JMapper | 3,205 |
Orika | 121 |
ModelMapper | 7 |
Dozer | 6.342 |
✅ MapStruct 吞吐量最高,JMapper 略低但依然远超其他。
6.3. 单次执行时间(SingleShotTime)
框架 | 单次耗时(ms/操作) |
---|---|
JMapper | 0.722 |
MapStruct | 2.111 |
Dozer | 16.311 |
ModelMapper | 22.342 |
Orika | 32.473 |
✅ JMapper 首次调用优势明显。
6.4. 采样时间(SampleTime)
框架 | p0.90 | p0.999 | p1.0 |
---|---|---|---|
JMapper | 0.001 | 0.006 | 3 |
MapStruct | 0.001 | 0.006 | 8 |
Orika | 0.007 | 0.143 | 14 |
ModelMapper | 0.138 | 0.991 | 15 |
Dozer | 0.131 | 0.954 | 7 |
✅ JMapper 在高并发、高负载下更稳定。
6.5. 结论
- ✅ JMapper 性能最强,尤其在单次和极端场景表现突出。
- ✅ MapStruct 稳定性高,吞吐量略胜,适合高并发服务。
- ❌ Dozer 和 ModelMapper 性能垫底,仅建议用于非核心链路或原型开发。
- ⚠️ **Orika 受限 Java 16+**,新项目不推荐。
7. 总结
本文对 5 款主流 Java 对象映射框架进行了真实场景下的性能压测:
- 首选推荐:
MapStruct
—— 编译期生成,性能强、类型安全、IDE 友好,社区活跃。 - 高性能场景:
JMapper
—— 适合对首次调用延迟敏感的场景,但需处理枚举等细节。 - 慎用:
Dozer
、ModelMapper
—— 反射开销大,性能差,仅适合简单项目。 - 观望:
Orika
—— 待 Java 16+ 兼容版本发布。
所有测试代码已开源:GitHub - baeldung/performance-tests