MapStruct如何基于前后缀规则实现动态映射避免重复@Mapping配置
MapStruct 固定规则映射免重复配置方案
这类固定命名规则下的重复注解编写问题不需要逐字段编写@Mapping,以下方案按实现成本从低到高排序,按需选择即可。
方案1:使用MapStruct通配符映射(推荐,MapStruct 1.5+支持)
1.5.0及以上版本的MapStruct支持*通配符做规则匹配,你只需要在映射方法上写3行通用规则配置,就能覆盖所有符合命名规则的字段,后续新增同结构字段也不需要改配置:
@Mapper public interface FinancialReportMapper { // 三行配置覆盖所有 子对象.属性 -> 子对象名+属性名 的映射 @Mapping(source = "dto.balanceSheetInfoDto.*.value", target = "*Value") @Mapping(source = "dto.balanceSheetInfoDto.*.isNA", target = "*IsNA") @Mapping(source = "dto.balanceSheetInfoDto.*.note", target = "*Note") FinancialReportTarget toTarget(FinancialReportDto dto); }
这三行配置会自动匹配你例子里的assets/liquidAssets,以及后续新增的所有同结构字段(比如固定资产、无形资产等),完全不需要逐字段写@Mapping。
方案2:抽取通用映射方法(兼容所有MapStruct版本)
如果你的项目用的MapStruct版本低于1.5,不支持通配符,可以把通用字段的映射逻辑抽成可复用的默认方法,新增字段时只需要传前缀调用一次即可,不需要写3个@Mapping。
- 先定义通用的指标字段映射逻辑:
public interface BaseIndicatorMapper { default void mapIndicator(IndicatorDto source, @MappingTarget Object target, String fieldPrefix) { Class<?> targetClz = target.getClass(); try { Field valueField = targetClz.getDeclaredField(fieldPrefix + "Value"); valueField.setAccessible(true); valueField.set(target, source.getValue()); Field isNaField = targetClz.getDeclaredField(fieldPrefix + "IsNA"); isNaField.setAccessible(true); isNaField.set(target, source.getIsNA()); Field noteField = targetClz.getDeclaredField(fieldPrefix + "Note"); noteField.setAccessible(true); noteField.set(target, source.getNote()); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException("指标字段映射失败,字段前缀:" + fieldPrefix, e); } } }
注:这里的IndicatorDto是你所有包含value/isNA/note三个属性的子对象的统一类型,也就是例子里assets/liquidAssets的共同类。
- 业务Mapper继承基础Mapper,在映射完成的回调里批量填充字段:
@Mapper public interface FinancialReportMapper extends BaseIndicatorMapper { FinancialReportTarget toTarget(FinancialReportDto dto); @AfterMapping default void fillIndicatorFields(FinancialReportDto dto, @MappingTarget FinancialReportTarget target) { BalanceSheetInfoDto balanceSheet = dto.getBalanceSheetInfoDto(); // 每个指标项只需要写一行调用,不需要重复写@Mapping mapIndicator(balanceSheet.getAssets(), target, "assets"); mapIndicator(balanceSheet.getLiquidAssets(), target, "liquidAssets"); // 后续新增同类型字段只需要在这里加一行调用即可 } }
方案3:自定义全局命名匹配策略(适合全项目统一规则的场景)
如果你整个项目里大量存在这类嵌套字段转平铺带前缀字段的映射规则,可以自定义MapStruct的字段匹配策略,编译期自动识别符合规则的字段,完全不需要写任何映射配置。
- 继承MapStruct默认的命名策略类,添加自定义匹配规则:
public class FlatFieldNamingStrategy extends DefaultAccessorNamingStrategy { @Override public boolean isPropertyNameMatch(String sourcePath, String targetName) { // 先走默认的精确匹配逻辑 if (super.isPropertyNameMatch(sourcePath, targetName)) { return true; } // 匹配 前缀.属性 -> 前缀+大写属性名 的规则 if (sourcePath.matches(".*\\.(value|isNA|note)$")) { String[] pathSegments = sourcePath.split("\\."); String generatedTargetName = pathSegments[pathSegments.length - 2] + StringUtils.capitalize(pathSegments[pathSegments.length - 1]); return generatedTargetName.equals(targetName); } return false; } }
- 在MapStruct的注解处理器配置里注册这个自定义策略,以Maven配置为例:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <compilerArgs> <arg>-Amapstruct.defaultAccessorNamingStrategy=com.xxx.config.FlatFieldNamingStrategy</arg> </compilerArgs> </configuration> </plugin>
配置完成后,整个项目里只要符合你定义的命名规则的字段,MapStruct都会自动完成映射,不需要写任何额外配置。
内容的提问来源于stack exchange,提问作者Akash jain
相关产品推荐
相关产品推荐

