在领域模型与持久层的 DO 之间拷贝数据是 java 开发常见的场景, 如果需要拷贝的字段过多, 我们往往寻求使用一些工具以代替赋值语句;
类似 spring 的 BeanUtils 使用反射实现自动赋值是一种经典的解决方案, 但在有性能要求的场景下存在性能瓶颈;
而 mapstruct 提出了另一种解决方案: 使用编译期预处理的方式自动生成拷贝转换的代码, 在节省拷贝代码的同时也保证了转换的性能;
基础映射能力
将一个 java bean 中的字段值映射到另一个 java bean 的同名同类型 (String 与枚举类型也可以相互映射) 的字段上;
如果 source bean 中的字段在 target bean 中没有同名同类型的对应字段, 则该字段无法映射到 target bean;
1 2 3 4 5
| @Mapper public interface TenantTransformer { TenantTransformer INSTANCE = Mappers.getMapper(TenantTransformer.class); Tenant toTenant(TenantDO tenantDO); }
|
高级用法
Mapping
@org.mapstruct.Mapping
注解可以在两个 bean 的不同名的字段之间建立映射关系;
1 2 3 4 5 6 7
| @Mapper public interface TenantTransformer { TenantTransformer INSTANCE = Mappers.getMapper(TenantTransformer.class); @Mapping(target = "name", source = "tenantName") Tenant toTenant(TenantDO tenantDO); }
|
当需要在两个 bean 的不同类型的字段建建立映射关系时, 需要同时配合使用 @org.mapstruct.Named
注解以申明类型转换方法:
1 2 3 4 5 6 7 8 9 10 11 12
| @Mapper public interface TenantTransformer { TenantTransformer INSTANCE = Mappers.getMapper(TenantTransformer.class); @Mapping(target = "tagSet", source = "tags", qualifiedByName = "toTagsSet") Tenant toTenant(TenantDO tenantDO); @Named("toTagsSet") static Set<String> toTagsSet(String tagsStr) { ...... } }
|
Context
当使用 @Named 注解定义类型转换方法时, 如果需要使用外部的实例时, 可以使用 @org.mapstruct.Context
注解引入外部实例:
1 2 3 4 5 6 7 8 9 10 11 12
| @Mapper public interface TenantTransformer { TenantTransformer INSTANCE = Mappers.getMapper(TenantTransformer.class); @Mapping(target = "tagSet", source = "tags", qualifiedByName = "toTagsSet") Tenant toTenant(TenantDO tenantDO, @Context Plan plan); @Named("toTagsSet") static Set<String> toTagsSet(String tagsStr, @Context Plan plan) { ...... } }
|
需要同时在主转换方法与 @Named 注解标注的类型转换方法上同时使用 @Context 引入外部实例;
expression
当需要将一个外部的实例映射到 targe bean 的某个字段时, 可以使用 @Mapping 注解的 exprssion
属性:
如下示例的含义是: 将 @Context 申明的外部实例 plan 映射到目标字段 plan 上;
1 2 3 4 5 6 7
| @Mapper public interface TenantTransformer { TenantTransformer INSTANCE = Mappers.getMapper(TenantTransformer.class); @Mapping(target = "plan", expression = "java(plan)") Tenant toTenant(TenantDO tenantDO, @Context Plan plan); }
|
具体案例
以下例子涵盖了上述基础映射能力及高级用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| @Mapper public interface APIMapper {
APIMapper INSTANCE = Mappers.getMapper(APIMapper.class);
@Mapping(target = "tags", source = "tags", qualifiedByName = "toTagsSet") @Mapping(target = "devStatus", source = "devStatus", qualifiedByName = "toDevStatus") @Mapping(target = "trafficDistributions", expression = "java(trafficDistributions)") @Mapping(target = "trafficGroupInstances", source = "config", qualifiedByName = "toTrafficGroups") API from(APIDO apiDO, @Context List<TrafficDistribution> trafficDistributions, @Context TrafficGroup.Type type);
@Named("toTagsSet") static Set<String> toTagsSet(String tagsStr) { return Sets.newHashSet(ConvertUtil.commaSplit(tagsStr)); }
@Named("toDevStatus") static Map<Env, API.DevStatus> toDevStatus(String devStatusStr) { return JSON.parseObject(devStatusStr, new TypeReference<>() {}); }
@Named("toTrafficGroups") static Map<Env, TrafficGroupInstance> toTrafficGroups(String config, @Context TrafficGroup.Type type) { final Map<Env, Map<String, Object>> featureConfigByEnv = JSON.parseObject(config, new TypeReference<>() {}); return featureConfigByEnv.entrySet().stream() .map(entry -> Pair.of(entry.getKey(), deserialize(entry.getValue(), type))) .collect(Collectors.toMap(Pair::getLeft, Pair::getRight)); }
static TrafficGroupInstance deserialize(Map<String, Object> config, TrafficGroup.Type type) { final TrafficGroupInstance.TrafficGroupFeatureTransformer<? extends TrafficGroupInstance> transformer = groupInstanceTransformers.get(type); return transformer.deserialize(config); }
@Mapping(target = "tags", source = "tags", qualifiedByName = "serializeTagSet") @Mapping(target = "devStatus", source = "devStatus", qualifiedByName = "serializeDevStatus") APIDO from(API api);
@Named("serializeDevStatus") static String serializeDevStatus(Map<Env, API.DevStatus> devStatus) { return JSON.toJSONString(devStatus); }
@Named("serializeTagSet") static String serializeTagSet(Set<String> tagSet) { return ConvertUtil.commaJoin(tagSet); }
}
|
参考链接