ModelMapper mapper.skip()在循环依赖POJO对象中失效问题求助
你遇到的这个问题其实是ModelMapper旧版本处理嵌套映射和已注册TypeMap复用的典型问题,结合你给出的映射信息,我来拆解下原因和解决办法:
问题根源
你分别给Wife和Husband注册了自映射的TypeMap,其中Wife的TypeMap只跳过了setHusband()。但ModelMapper在处理Husband的wife属性时,会自动复用已存在的Wife TypeMap规则——这就导致它不仅不会跳过Husband的setWife(),反而会尝试去映射wife对象的所有非跳过属性(包括wife的husband引用),形成了隐性的循环映射,最终覆盖了你原本设置的skip(Husband::setWife)规则。
解决方案
这里给你三种可行的解决思路,按推荐程度排序:
1. 升级ModelMapper版本(最省心)
ModelMapper 1.1.3是2017年的旧版本,后续的2.x、3.x系列对循环引用的处理做了大幅优化,会自动检测双向引用并跳过,不需要手动配置skip规则。
把你的依赖升级到最新稳定版(比如3.2.0),然后去掉所有手动创建的TypeMap代码,直接使用默认映射即可:
public class ModelTest { @Test public void test() { ModelMapper modelMapper = new ModelMapper(); Wife wife = Wife.builder().age(25).name("Sarah").woman("good woman").build(); Husband husband = Husband.builder().age(28).name("Imtiaz").man("good man").build(); wife.setHusband(husband); husband.setWife(wife); Husband updatedHusband = Husband.builder().age(28).name("Imtiaz Shakil").man("slightly good man").build(); modelMapper.map(updatedHusband, husband); System.out.println(husband.toString()); System.out.println(husband.getWife().toString()); } }
升级后,ModelMapper会自动保留原有的双向引用,同时正确映射其他属性。
2. 配置全局属性跳过规则
如果你无法升级版本,可以通过全局配置来统一跳过双向引用的属性,避免单独注册TypeMap带来的冲突:
ModelMapper modelMapper = new ModelMapper(); modelMapper.getConfiguration().setPropertyCondition(context -> { // 跳过Husband的wife属性和Wife的husband属性 PropertyInfo dest = context.getDestination(); return !( (dest.getDeclaringType() == Husband.class && dest.getName().equals("wife")) || (dest.getDeclaringType() == Wife.class && dest.getName().equals("husband")) ); });
这个配置会让ModelMapper在所有映射场景中,都跳过这两个双向引用属性,从根源上避免循环映射的触发。
3. 修改TypeMap配置,跳过整个嵌套属性
如果你一定要保留单独的TypeMap,需要在Husband的TypeMap中明确阻止对wife属性的深层映射,而不仅仅是跳过setWife()方法:
ModelMapper modelMapper = new ModelMapper(); // 配置Wife的TypeMap,跳过husband属性 TypeMap<Wife, Wife> typeWife = modelMapper.createTypeMap(Wife.class, Wife.class); typeWife.addMappings(mapper -> mapper.skip(Wife::setHusband)); // 配置Husband的TypeMap,跳过整个wife属性的映射(包括子属性) TypeMap<Husband, Husband> typeHusband = modelMapper.createTypeMap(Husband.class, Husband.class); typeHusband.addMappings(mapper -> { // 直接跳过wife属性的所有映射操作 mapper.skip(Husband::setWife); });
这样就能阻止ModelMapper去触发Wife的TypeMap,确保你的skip规则生效。
验证效果
无论选择哪种方案,运行你的测试代码后,husband的name和man属性会被更新为updatedHusband的值,而wife引用会保持原对象不变,不会出现被覆盖或循环修改的问题。
内容的提问来源于stack exchange,提问作者Imtiaz Shakil Siddique

