1. mapstruct 简介与核心概念
1.1 什么是 mapstruct
mapstruct 是一个基于注解的 java 对象映射工具,它通过在编译时生成映射代码来实现对象之间的转换。与其他映射框架(如 modelmapper)相比,mapstruct 的主要优势在于:
- 编译时生成代码:性能接近手写代码
- 类型安全:编译时检查映射的正确性
- 零运行时依赖:生成的代码不依赖 mapstruct
- 丰富的映射功能:支持复杂映射场景
1.2 核心概念解析
映射器 (mapper)
- 负责对象转换的核心接口
- 通过
@mapper注解标识 - 编译时生成实现类
映射方法 (mapping method)
- 在映射器接口中定义的具体转换方法
- 支持单个对象、集合、map 等转换
转换规则 (mapping rules)
- 通过注解配置的属性映射规则
- 支持自定义转换逻辑
2. 环境搭建与基础配置
2.1 依赖配置
maven 配置
<properties>
<org.mapstruct.version>1.5.5.final</org.mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupid>org.mapstruct</groupid>
<artifactid>mapstruct</artifactid>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-compiler-plugin</artifactid>
<version>3.11.0</version>
<configuration>
<annotationprocessorpaths>
<path>
<groupid>org.mapstruct</groupid>
<artifactid>mapstruct-processor</artifactid>
<version>${org.mapstruct.version}</version>
</path>
</annotationprocessorpaths>
</configuration>
</plugin>
</plugins>
</build>gradle 配置
plugins {
id 'java'
}
dependencies {
implementation 'org.mapstruct:mapstruct:1.5.5.final'
annotationprocessor 'org.mapstruct:mapstruct-processor:1.5.5.final'
}
compilejava {
options.annotationprocessorpath = configurations.annotationprocessor
}2.2 spring boot 集成
@configuration
public class mapstructconfig {
@bean
public usermapper usermapper() {
return mappers.getmapper(usermapper.class);
}
}或者使用组件扫描:
@mapper(componentmodel = "spring")
public interface usermapper {
// 映射方法
}3. 基础映射功能详解
3.1 简单对象映射
源对象和目标对象定义
// 源对象
@data
@allargsconstructor
@noargsconstructor
public class user {
private long id;
private string username;
private string email;
private string password;
private localdatetime createtime;
private integer status;
}
// 目标对象
@data
@allargsconstructor
@noargsconstructor
public class userdto {
private long id;
private string username;
private string email;
private string createtime;
private string statusdesc;
}基础映射器
@mapper(componentmodel = "spring")
public interface usermapper {
usermapper instance = mappers.getmapper(usermapper.class);
// 基础映射 - 属性名相同时自动映射
userdto todto(user user);
// 反向映射
user toentity(userdto userdto);
}3.2 属性名不同的映射
@mapper(componentmodel = "spring")
public interface usermapper {
@mapping(source = "username", target = "name")
@mapping(source = "createtime", target = "registrationdate")
@mapping(source = "status", target = "userstatus")
userdto todto(user user);
@mapping(source = "name", target = "username")
@mapping(source = "registrationdate", target = "createtime")
@mapping(source = "userstatus", target = "status")
user toentity(userdto userdto);
}3.3 类型转换
内置类型转换
@mapper(componentmodel = "spring")
public interface usermapper {
// 自动类型转换:localdatetime -> string
@mapping(source = "createtime", target = "createtimestr")
// 数字转字符串
@mapping(source = "status", target = "statusstr")
userdto todto(user user);
// 使用表达式进行复杂转换
@mapping(target = "statusdesc",
expression = "java(convertstatus(user.getstatus()))")
userdto todtowithexpression(user user);
default string convertstatus(integer status) {
switch (status) {
case 0: return "禁用";
case 1: return "启用";
default: return "未知";
}
}
}4. 高级映射功能
4.1 嵌套对象映射
复杂对象结构
// 地址对象
@data
public class address {
private string province;
private string city;
private string street;
private string zipcode;
}
// 用户详情对象
@data
public class userdetail {
private long id;
private string username;
private address address;
private list<string> hobbies;
}
// dto 对象
@data
public class userdetaildto {
private long id;
private string username;
private string province;
private string city;
private string street;
private string zipcode;
private string hobbies;
}嵌套映射配置
@mapper(componentmodel = "spring")
public interface userdetailmapper {
@mapping(source = "address.province", target = "province")
@mapping(source = "address.city", target = "city")
@mapping(source = "address.street", target = "street")
@mapping(source = "address.zipcode", target = "zipcode")
@mapping(source = "hobbies", target = "hobbies")
userdetaildto todto(userdetail userdetail);
// 反向映射
@mapping(target = "address.province", source = "province")
@mapping(target = "address.city", source = "city")
@mapping(target = "address.street", source = "street")
@mapping(target = "address.zipcode", source = "zipcode")
userdetail toentity(userdetaildto dto);
}4.2 集合映射
列表和数组映射
@mapper(componentmodel = "spring")
public interface usermapper {
// 列表映射
list<userdto> todtolist(list<user> users);
// 集合映射
set<userdto> todtoset(set<user> users);
// 数组映射
userdto[] todtoarray(user[] users);
}map 映射
@mapper(componentmodel = "spring")
public interface usermapper {
// map 值映射
@mapping(source = "value", target = "value")
map<string, userdto> todtomap(map<string, user> usermap);
// map 键值都映射
@mapmapping(keytargettype = string.class, valuetargettype = userdto.class)
map<string, userdto> todtofullmap(map<long, user> usermap);
}4.3 多源对象映射
@data
public class userbasicinfo {
private string firstname;
private string lastname;
private integer age;
}
@data
public class usercontactinfo {
private string email;
private string phone;
private address address;
}
@data
public class completeuserdto {
private string fullname;
private integer age;
private string email;
private string phone;
private string address;
}
@mapper(componentmodel = "spring")
public interface multisourcemapper {
@mapping(target = "fullname", expression = "java(basicinfo.getfirstname() + \" \" + basicinfo.getlastname())")
@mapping(source = "basicinfo.age", target = "age")
@mapping(source = "contactinfo.email", target = "email")
@mapping(source = "contactinfo.phone", target = "phone")
@mapping(target = "address", expression = "java(formataddress(contactinfo.getaddress()))")
completeuserdto tocompletedto(userbasicinfo basicinfo, usercontactinfo contactinfo);
default string formataddress(address address) {
return address.getprovince() + address.getcity() + address.getstreet();
}
}5. 自定义映射与高级配置
5.1 自定义映射方法
@mapper(componentmodel = "spring")
public abstract class customusermapper {
// 使用抽象类以便实现自定义方法
public abstract userdto todto(user user);
public abstract list<userdto> todtolist(list<user> users);
// 自定义映射逻辑
public userdto tocustomdto(user user) {
if (user == null) {
return null;
}
userdto dto = new userdto();
dto.setid(user.getid());
dto.setusername(user.getusername().touppercase()); // 自定义处理
dto.setemail(user.getemail());
dto.setcreatetime(formatdatetime(user.getcreatetime()));
return dto;
}
protected string formatdatetime(localdatetime datetime) {
if (datetime == null) {
return null;
}
datetimeformatter formatter = datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss");
return datetime.format(formatter);
}
}5.2 使用表达式和常量
@mapper(componentmodel = "spring")
public interface expressionmapper {
// 使用常量
@mapping(target = "sourcesystem", constant = "web_application")
// 使用默认值
@mapping(source = "status", target = "status", defaultvalue = "1")
// 使用表达式
@mapping(target = "createtime",
expression = "java(java.time.localdatetime.now())")
@mapping(target = "fullname",
expression = "java(user.getfirstname() + \" \" + user.getlastname())")
userdto todto(user user);
// 条件映射
@mapping(target = "email",
condition = "java(user.getemail() != null && user.getemail().contains(\"@\"))")
userdto todtoconditional(user user);
}5.3 继承映射配置
// 基础映射配置
@mapperconfig
public interface basemapperconfig {
@mapping(target = "createtime", ignore = true)
@mapping(target = "updatetime", ignore = true)
user toentity(userdto dto);
}
// 具体映射器继承配置
@mapper(componentmodel = "spring", config = basemapperconfig.class)
public interface usermapper extends basemapperconfig {
// 继承基础配置,可以覆盖或添加新映射
@mapping(target = "createtime", source = "registrationdate")
@override
user toentity(userdto dto);
// 新增映射方法
userdto todto(user user);
}6. spring boot 集成实践
6.1 配置类定义
@configuration
@mapperscan(basepackages = "com.example.mapper")
public class mapstructautoconfiguration {
@bean
@primary
public usermapper usermapper() {
return new usermapperimpl();
}
@bean
public productmapper productmapper() {
return new productmapperimpl();
}
}6.2 服务层使用
@service
@transactional
public class userservice {
private final userrepository userrepository;
private final usermapper usermapper;
public userservice(userrepository userrepository, usermapper usermapper) {
this.userrepository = userrepository;
this.usermapper = usermapper;
}
public userdto createuser(usercreaterequest request) {
user user = usermapper.toentity(request);
user.setcreatetime(localdatetime.now());
user.setstatus(1);
user saveduser = userrepository.save(user);
return usermapper.todto(saveduser);
}
public userdto getuserbyid(long id) {
return userrepository.findbyid(id)
.map(usermapper::todto)
.orelsethrow(() -> new resourcenotfoundexception("用户不存在"));
}
public page<userdto> getusers(pageable pageable) {
page<user> userpage = userrepository.findall(pageable);
return userpage.map(usermapper::todto);
}
public list<userdto> searchusers(usersearchcriteria criteria) {
list<user> users = userrepository.findbycriteria(criteria);
return usermapper.todtolist(users);
}
}6.3 控制器层应用
@restcontroller
@requestmapping("/api/users")
@validated
public class usercontroller {
private final userservice userservice;
public usercontroller(userservice userservice) {
this.userservice = userservice;
}
@postmapping
public responseentity<userdto> createuser(@valid @requestbody usercreaterequest request) {
userdto userdto = userservice.createuser(request);
return responseentity.status(httpstatus.created).body(userdto);
}
@getmapping("/{id}")
public responseentity<userdto> getuser(@pathvariable long id) {
userdto userdto = userservice.getuserbyid(id);
return responseentity.ok(userdto);
}
@getmapping
public responseentity<page<userdto>> getusers(
@requestparam(defaultvalue = "0") int page,
@requestparam(defaultvalue = "10") int size,
@requestparam(defaultvalue = "id") string sort) {
pageable pageable = pagerequest.of(page, size, sort.by(sort));
page<userdto> userpage = userservice.getusers(pageable);
return responseentity.ok(userpage);
}
@putmapping("/{id}")
public responseentity<userdto> updateuser(
@pathvariable long id,
@valid @requestbody userupdaterequest request) {
userdto userdto = userservice.updateuser(id, request);
return responseentity.ok(userdto);
}
}7. 复杂业务场景实战
7.1 订单系统映射
领域模型
// 订单实体
@data
@entity
public class order {
@id
@generatedvalue(strategy = generationtype.identity)
private long id;
private string orderno;
private bigdecimal totalamount;
private integer status;
private localdatetime createtime;
private localdatetime updatetime;
@onetomany(mappedby = "order", cascade = cascadetype.all)
private list<orderitem> items;
@manytoone
@joincolumn(name = "user_id")
private user user;
}
// 订单项实体
@data
@entity
public class orderitem {
@id
@generatedvalue(strategy = generationtype.identity)
private long id;
@manytoone
@joincolumn(name = "order_id")
private order order;
@manytoone
@joincolumn(name = "product_id")
private product product;
private integer quantity;
private bigdecimal price;
private bigdecimal subtotal;
}
// 订单 dto
@data
public class orderdto {
private long id;
private string orderno;
private bigdecimal totalamount;
private string statusdesc;
private string createtime;
private string updatetime;
private string customername;
private string customeremail;
private list<orderitemdto> items;
}
// 订单项 dto
@data
public class orderitemdto {
private long productid;
private string productname;
private integer quantity;
private bigdecimal price;
private bigdecimal subtotal;
}复杂映射器
@mapper(componentmodel = "spring", uses = {productmapper.class})
public interface ordermapper {
@mapping(source = "user.username", target = "customername")
@mapping(source = "user.email", target = "customeremail")
@mapping(target = "statusdesc", expression = "java(convertorderstatus(order.getstatus()))")
@mapping(source = "createtime", target = "createtime", dateformat = "yyyy-mm-dd hh:mm:ss")
@mapping(source = "updatetime", target = "updatetime", dateformat = "yyyy-mm-dd hh:mm:ss")
orderdto todto(order order);
list<orderdto> todtolist(list<order> orders);
@mapping(source = "product.id", target = "productid")
@mapping(source = "product.name", target = "productname")
orderitemdto itemtodto(orderitem item);
list<orderitemdto> itemstodto(list<orderitem> items);
default string convertorderstatus(integer status) {
switch (status) {
case 0: return "待支付";
case 1: return "已支付";
case 2: return "已发货";
case 3: return "已完成";
case 4: return "已取消";
default: return "未知状态";
}
}
// 更新映射 - 只更新非空字段
@mapping(target = "id", ignore = true)
@mapping(target = "orderno", ignore = true)
@mapping(target = "createtime", ignore = true)
void updateorderfromdto(orderupdatedto dto, @mappingtarget order order);
}7.2 继承体系映射
继承结构对象
// 基础支付类
@data
public abstract class payment {
private long id;
private bigdecimal amount;
private integer status;
private localdatetime createtime;
}
// 支付宝支付
@data
@equalsandhashcode(callsuper = true)
public class alipaypayment extends payment {
private string alipaytradeno;
private string buyerid;
private string buyeremail;
}
// 微信支付
@data
@equalsandhashcode(callsuper = true)
public class wechatpayment extends payment {
private string wechattradeno;
private string openid;
private string appid;
}
// 支付 dto
@data
public class paymentdto {
private long id;
private bigdecimal amount;
private string statusdesc;
private string createtime;
private string paymenttype;
private map<string, object> extrainfo;
}继承映射器
@mapper(componentmodel = "spring")
public interface paymentmapper {
// 使用子类映射器
alipaymapper alipaymapper();
wechatmapper wechatmapper();
default paymentdto todto(payment payment) {
if (payment instanceof alipaypayment) {
return alipaymapper.todto((alipaypayment) payment);
} else if (payment instanceof wechatpayment) {
return wechatmapper.todto((wechatpayment) payment);
} else {
return basetodto(payment);
}
}
@mapping(target = "paymenttype", constant = "base")
@mapping(target = "extrainfo", ignore = true)
paymentdto basetodto(payment payment);
list<paymentdto> todtolist(list<payment> payments);
}
@mapper(componentmodel = "spring")
public interface alipaymapper {
@mapping(target = "paymenttype", constant = "alipay")
@mapping(target = "extrainfo", expression = "java(createalipayextrainfo(alipaypayment))")
paymentdto todto(alipaypayment alipaypayment);
default map<string, object> createalipayextrainfo(alipaypayment payment) {
map<string, object> extra = new hashmap<>();
extra.put("alipaytradeno", payment.getalipaytradeno());
extra.put("buyerid", payment.getbuyerid());
extra.put("buyeremail", payment.getbuyeremail());
return extra;
}
}
@mapper(componentmodel = "spring")
public interface wechatmapper {
@mapping(target = "paymenttype", constant = "wechat")
@mapping(target = "extrainfo", expression = "java(createwechatextrainfo(wechatpayment))")
paymentdto todto(wechatpayment wechatpayment);
default map<string, object> createwechatextrainfo(wechatpayment payment) {
map<string, object> extra = new hashmap<>();
extra.put("wechattradeno", payment.getwechattradeno());
extra.put("openid", payment.getopenid());
extra.put("appid", payment.getappid());
return extra;
}
}8. 性能优化与最佳实践
8.1 性能优化策略
批量映射优化
@mapper(componentmodel = "spring")
public interface optimizedmapper {
// 使用 @named 注解优化重复映射
@named("touserdto")
@mapping(target = "statusdesc", expression = "java(convertstatus(user.getstatus()))")
userdto touserdto(user user);
// 批量映射使用相同逻辑
list<userdto> touserdtolist(list<user> users);
// 避免在循环中调用复杂逻辑
default list<userdto> optimizedtodtolist(list<user> users) {
if (users == null) {
return null;
}
list<userdto> list = new arraylist<>(users.size());
for (user user : users) {
list.add(touserdto(user));
}
return list;
}
}懒加载处理
@mapper(componentmodel = "spring")
public interface lazyloadingmapper {
@mapping(target = "orderitems", expression = "java(handlelazyitems(order.getitems()))")
orderdto todto(order order);
default list<orderitemdto> handlelazyitems(list<orderitem> items) {
// 处理 hibernate 懒加载
if (items instanceof persistentcollection && !((persistentcollection) items).wasinitialized()) {
return collections.emptylist();
}
return itemstodto(items);
}
list<orderitemdto> itemstodto(list<orderitem> items);
}8.2 最佳实践
配置统一管理
@mapperconfig(
componentmodel = "spring",
unmappedtargetpolicy = reportingpolicy.ignore,
nullvaluepropertymappingstrategy = nullvaluepropertymappingstrategy.ignore
)
public interface centralconfig {
@mapping(target = "id", ignore = true)
@mapping(target = "createtime", ignore = true)
@mapping(target = "updatetime", ignore = true)
void toentity(object dto, @mappingtarget object entity);
}
@mapper(config = centralconfig.class)
public interface usermapper extends centralconfig {
// 具体映射方法
}错误处理
@mapper(componentmodel = "spring")
public interface safemapper {
default userdto safetodto(user user) {
try {
return todto(user);
} catch (exception e) {
log.error("对象转换失败: {}", user, e);
return createfallbackdto(user);
}
}
userdto todto(user user);
default userdto createfallbackdto(user user) {
userdto dto = new userdto();
if (user != null) {
dto.setid(user.getid());
dto.setusername("转换失败");
}
return dto;
}
}9. 测试策略
9.1 单元测试
@extendwith(mockitoextension.class)
class usermappertest {
@injectmocks
private usermapperimpl usermapper;
@test
void testtodto() {
// 准备测试数据
user user = new user();
user.setid(1l);
user.setusername("testuser");
user.setemail("test@example.com");
user.setcreatetime(localdatetime.now());
user.setstatus(1);
// 执行转换
userdto dto = usermapper.todto(user);
// 验证结果
assertthat(dto.getid()).isequalto(1l);
assertthat(dto.getusername()).isequalto("testuser");
assertthat(dto.getemail()).isequalto("test@example.com");
assertthat(dto.getstatusdesc()).isequalto("启用");
}
@test
void testtodtolist() {
list<user> users = arrays.aslist(
createuser(1l, "user1"),
createuser(2l, "user2")
);
list<userdto> dtos = usermapper.todtolist(users);
assertthat(dtos).hassize(2);
assertthat(dtos.get(0).getusername()).isequalto("user1");
assertthat(dtos.get(1).getusername()).isequalto("user2");
}
private user createuser(long id, string username) {
user user = new user();
user.setid(id);
user.setusername(username);
user.setemail(username + "@example.com");
user.setstatus(1);
return user;
}
}9.2 集成测试
@springboottest
@transactional
class usermapperintegrationtest {
@autowired
private usermapper usermapper;
@autowired
private userrepository userrepository;
@test
void testentitytodtomapping() {
// 创建并保存实体
user user = new user();
user.setusername("integrationuser");
user.setemail("integration@example.com");
user.setpassword("password");
user.setcreatetime(localdatetime.now());
user.setstatus(1);
user saveduser = userrepository.save(user);
// 测试映射
userdto dto = usermapper.todto(saveduser);
assertthat(dto.getid()).isnotnull();
assertthat(dto.getusername()).isequalto("integrationuser");
assertthat(dto.getemail()).isequalto("integration@example.com");
}
}10. 常见问题与解决方案
10.1 编译问题
问题1:映射器实现类未生成
// 解决方案:检查注解处理器配置
@mapper(componentmodel = "spring") // 确保使用正确注解
public interface usermapper {
userdto todto(user user);
}问题2:依赖冲突
<!-- 确保 mapstruct 版本兼容 -->
<properties>
<org.mapstruct.version>1.5.5.final</org.mapstruct.version>
</properties>10.2 运行时问题
问题1:空指针异常
@mapper(componentmodel = "spring")
public interface safemapper {
// 使用 nullvaluepropertymappingstrategy 避免 npe
@mapping(target = "statusdesc",
nullvaluepropertymappingstrategy = nullvaluepropertymappingstrategy.set_to_null)
userdto todto(user user);
}问题2:循环依赖
// 使用 @context 避免循环依赖
@mapper(componentmodel = "spring")
public interface ordermapper {
orderdto todto(order order, @context cycleavoidingmappingcontext context);
}
// 循环依赖上下文
public class cycleavoidingmappingcontext {
private map<object, object> knowninstances = new identityhashmap<>();
@suppresswarnings("unchecked")
public <t> t getmappedinstance(object source, class<t> targettype) {
return (t) knowninstances.get(source);
}
public void storemappedinstance(object source, object target) {
knowninstances.put(source, target);
}
}总结
mapstruct 是一个功能强大、性能优异的对象映射框架,特别适合在 spring boot 项目中使用。通过本文的详细讲解,您应该已经掌握了
- 基础配置和简单映射 - 快速上手基础功能
- 高级映射特性 - 处理复杂业务场景
- spring boot 集成 - 在实际项目中的应用
- 性能优化 - 提升系统性能的最佳实践
- 测试策略 - 保证代码质量的方法
- 问题解决 - 应对常见问题的方案
到此这篇关于springboot 对象转换 mapstruct的实现小结的文章就介绍到这了,更多相关springboot 对象转换 mapstruct内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论