ModelMapper自定义映射报错及转换器未触发问题求助
我来帮你逐个解决这两个ModelMapper的问题:
问题1:自定义映射报错的解决方案
这个报错的核心原因很明确:ModelMapper对SourceGetter的校验很严格,它要求你引用的是源实体类自己定义的属性getter方法,而getClass()是Object类的方法,并不是Contact的属性getter,所以直接用Contact::getClass或者lambda引用它就会触发Illegal SourceGetter defined错误。
你可以换个思路,把整个源对象传入转换器,在转换器里获取类名,修改后的代码如下:
modelMapper.typeMap(Contact.class, ContactView.class).addMappings(mapper -> { // 定义转换器:接收Contact对象,返回类名字符串 Converter<Contact, String> classToSimpleName = ctx -> { Contact source = ctx.getSource(); return source == null ? null : source.getClass().getSimpleName(); }; // 把整个Contact对象作为源传给转换器,映射到type字段 mapper.using(classToSimpleName).map(src -> src, ContactView::setType); });
这里的关键是src -> src,它把整个源对象传递给Converter,绕过了ModelMapper对SourceGetter的属性校验。
如果你的ModelMapper版本是2.0+,也可以直接简化成lambda映射,不需要显式定义Converter:
modelMapper.typeMap(Contact.class, ContactView.class).addMappings(mapper -> { mapper.map(src -> src.getClass().getSimpleName(), ContactView::setType); });
如果这个写法还是报错,建议升级到ModelMapper的最新稳定版——旧版本对lambda作为SourceGetter的支持不够完善。
问题2:转换器未触发的排查与修复
你的Converter没被触发,主要有几个常见的坑,逐个修复即可:
1. 非静态内部类的访问问题
你的ContactConverter是Mapper配置类的非静态内部类,ModelMapper使用它时需要依赖外部Mapper类的实例,很可能导致实例化失败或无法被正确识别。把它改成静态内部类,或者单独抽成独立类:
@Configuration public class Mapper { // ... 其他代码 ... // 改成静态内部类,避免依赖外部实例 static class ContactConverter implements Converter<Contact, ContactView> { private final ModelMapper localMapper; private final ContactTypeRepository contactTypeRepository; // 通过构造注入依赖,不要自己new ModelMapper! public ContactConverter(ModelMapper localMapper, ContactTypeRepository contactTypeRepository) { this.localMapper = localMapper; this.contactTypeRepository = contactTypeRepository; } @Override public ContactView convert(MappingContext<Contact, ContactView> context) { Contact contact = context.getSource(); ContactView contactView = localMapper.map(contact, ContactView.class); ContactType contactType = contactTypeRepository.getByCode(contact.getClass().getSimpleName().toLowerCase()); contactView.setType(localMapper.map(contactType, ReferenceEntityView.class)); return contactView; } } }
2. 不要在Converter中新建ModelMapper实例
你之前在Converter里new ModelMapper()会创建一个全新的、没有任何自定义配置的实例,不仅浪费资源,还会导致映射逻辑和主配置的ModelMapper不一致,必须通过构造注入把主配置的ModelMapper传进来。
3. TypeMap优先级高于全局Converter
ModelMapper的映射优先级是:自定义TypeMap > 全局Converter > 自动映射。你已经为Person(Contact的子类)定义了Person -> PersonView的TypeMap,如果PersonView是ContactView的子类,那么映射Person实例到ContactView时,ModelMapper会优先匹配Person -> PersonView的TypeMap,不会触发Contact -> ContactView的Converter。
解决方法二选一:
- 把
Person -> PersonView的映射逻辑整合到ContactConverter中; - 在
Person -> PersonView的TypeMap中手动添加type字段的映射:
modelMapper.typeMap(Person.class, PersonView.class).addMappings(mapper -> { mapper.skip(PersonView::setName); mapper.map(Person::getName, PersonView::setLastName); // 手动添加type字段映射 mapper.map(src -> src.getClass().getSimpleName(), PersonView::setType); });
4. 确保Converter被正确注册
修改Bean配置,通过构造注入依赖创建Converter:
@Bean public ModelMapper getMapper() { ModelMapper modelMapper = new ModelMapper(); modelMapper.getConfiguration() .setMatchingStrategy(MatchingStrategies.STRICT); // ... 其他TypeMap配置 ... // 构造注入依赖创建Converter并注册 ContactConverter contactConverter = new ContactConverter(modelMapper, contactTypeRepository); modelMapper.addConverter(contactConverter); return modelMapper; }
做完这些修改后,你的Converter应该就能正常触发了。
内容的提问来源于stack exchange,提问作者dr.fatca

