modelmapper-定义显式Map时出现非法sourcegetter错误

m528fe3b  于 2021-06-27  发布在  Java
关注(0)|答案(2)|浏览(401)

第1部分
我正在使用JavaModelMapper库(http://modelmapper.org/)管理实体和DTO之间的Map。我有一个联系人(实体)和一个联系人视图(dto)。我在contactview中有一个名为“type”的字符串字段,它在contact中不存在。它的值应该只是实体子类的名称。我试着这样做定制Map:

modelMapper.typeMap(Contact.class, ContactView.class).addMappings(mapper -> {   
   mapper.map(src -> src.getClass().getSimpleName(), ContactView::setType);
});

我在mapper.map(src->src.getclass().getsimplename(),contactview::settype)处得到一个编译错误;
定义了非法的sourcegetter
org.springframework.beans.factory.support.simpleinstationstrategy.instantiate(simpleinstationstrategy)出现1个错误。java:185)~[ Spring Bean -5.3.2。jar:5.3.2]在org.springframework.beans.factory.support.constructorresolver.instantiate(constructorresolver。java:653)~[ Spring Bean -5.3.2。jar:5.3.2] ... 省略33个公共框架
我甚至试过使用转换器,同样的结果:

modelMapper.typeMap(Contact.class, ContactView.class).addMappings(mapper -> {
  Converter<Class, String> toName = ctx -> ctx.getSource() == null ? null : ctx.getSource().getSimpleName();
  mapper.using(toName).map(Contact::getClass, ContactView::setType);
});

你知道怎么解决这个问题吗?
第2部分
根据建议的答案,我尝试向modelmapper添加一个转换器类。这是我配置modelmapper bean的地方:

@Configuration
public class Mapper {
    @Autowired
    private ContactTypeRepository contactTypeRepository;

    @Bean
    public ModelMapper getMapper() {
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration()
                .setMatchingStrategy(MatchingStrategies.STRICT);

        modelMapper.typeMap(ContactTag.class, ReferenceEntityView.class).addMappings(mapper -> {
            mapper.map(src -> src.getTag().getCode(), ReferenceEntityView::setCode);
            mapper.map(src -> src.getTag().getValue(), ReferenceEntityView::setValue);
        });

        modelMapper.typeMap(Person.class, PersonView.class).addMappings(mapper -> {
            mapper.skip(PersonView::setName);
            mapper.map(Person::getName, PersonView::setLastName);
        });

        modelMapper.addConverter(new ContactConverter());

        return modelMapper;
    }

    class ContactConverter implements Converter<Contact, ContactView>  {
        private ModelMapper localMapper = new ModelMapper();

        @Override
        public ContactView convert(MappingContext<Contact, ContactView> context) {
            Contact contact = context.getSource();
            ContactView contactView = localMapper.map(contact, ContactView.class);
            ContactType contactType = contactTypeRepository.getByCode(context.getSource().getClass().getSimpleName().toLowerCase());
            contactView.setType(localMapper.map(contactType, ReferenceEntityView.class));
            return contactView;
        }
    }
}

这就是我使用modelmapper bean生成dto的地方:

@RestController
@RequestMapping(value = "/contacts")
public class ContactController {
    @Autowired
    private ContactRepository contactRepository;
    @Autowired
    private ModelMapper modelMapper;

    @GetMapping(value = "/{id}")
    @ResponseStatus(HttpStatus.OK)
    public ContactView findById(@PathVariable("id") Long id){
        Contact c = contactRepository.getOne(id);
        ContactView cv = modelMapper.map(c, ContactView.class);
        return cv;
    }
}

由于某些原因,转换器的convert方法未被调用,contactview对象的“type”字段为空。modelmapperbean上的其他Map工作正常。

l7wslrjt

l7wslrjt1#

试试我的图书馆便条。它是一个注解处理器。这意味着jdk将在编译项目之前为您生成类。所以它在编译时完成工作。您可以像自己编写的任何其他类一样使用生成的类。您可以看到生成的类的源代码,因此不再具有魔力。这个库能够为您生成dto类。你不需要改变原来的班级。除了在原始类上配置注解外,还可以选择创建新的配置类,并在其上配置注解。该库支持复制和继承原始类的所有属性,并在此基础上删除、修改或添加属性。对于您的问题:

// this will generate a DTO class named "ContactView". 
// (This is the default name which just append 'View') 
// You can change this name using genName attribute.
@ViewOf(value=Contact.class, includePattern = ".*")
public class ContactViewConfigure {
    // Add a new property. 
    // Make sure there is no property named 'type' already in Contact. 
    // Or you need use @OverrideViewProperty
    // There are multi way to add new property.
    // In this way, you use a public static method accept the original class instance as the unique argument. 
    // The new property name is decide by the attribute 'type' of @NewViewProperty. 
    // So the method name is not important.
    @NewViewProperty("type")
    public static String type(Contact contact) {
        return contact.getClass().getSimpleName()
    }
}

// This is the generated class.
public class ContactView {
    // other properties
    ...
    private String type;
    // getters and setters (By default only getters are generated)
    ...
    // many constructors
    ...
    public static ContactView read(Contact source) {
        ContactView out = new ContactView();
        // initialize other properties
        ...
        out.type = ContactViewConfigure.type(source);
        // initialize other properties
        ...
        return out;
    }

    // other read method, such as read list, set and map.
    ...
    // other generated methods
    ...
}

// use like this.
Contact contact = ...
ContactView dto = ContactView.read(contact);

在某些情况下,beanknife比modelmapper强大得多。例如,如果发生了错误,可以检查生成类的源代码(通常位于/target/generated source/annotations中,在ide上可能不同),并查看原因。如果它真的是一个bug,你可以把这个问题提交给github,我会尽快处理的。
这里有更多的例子。

nhjlsmyf

nhjlsmyf2#

这是因为modelmapper的实现

public boolean isValid(M member) {
  return !Modifier.isStatic(member.getModifiers()) && !member.isSynthetic();
}

issynthetic方法的文档中说
如果此成员是由编译器引入的,则返回true;否则返回false。当且仅当编译器引入此成员时返回:true。
我想这就是为什么它不能例外。
对于类似的情况,我们引入了一个特定的Map器类,使用modelmapper作为基本Map器,并设置另一个字段:

class ContactMapper{
 ...
 public ContactView toView(Contact contact){
 ContactView contactView = modelMapper.map(contact,ContactView.class);
 contactView.setType(contact.getClass().getSimpleName());
 return contactView;
 }

为了使它与整体Map保持一致,您可以将其定义为一个转换器,并像这样将其注册到Map中

class ContactConverter implements Converter<Contact, ContactView>  {
        private final ModelMapper localMapper = new ModelMapper();

        @Override
        public ContactView convert(MappingContext<Contact, ContactView> context) {
            Contact contact = context.getSource();
            ContactView contactView = localMapper.map(contact, ContactView.class);
            contactView.setType(contact.getClass().getSimpleName());
            return contactView;
        }
    }

    ModelMapper modelMapper = new ModelMapper();
    modelMapper.addConverter(new ContactConverter());

相关问题