发现Mapstruct的一个bug

博客 分享
0 146
张三
张三 2022-07-12 14:01:43
悬赏:0 积分 收藏

发现Mapstruct的一个bug

前言


在一次需求的开发过程中,发现一个对象插入数据库时某个字段总是为空。简化后的代码如下:

@Autowired    private PersonService personService;        public void test1(){        Person person = personService.findById(1L);        PersonDto personDto = PersonMapper.INSTANCE.personToPersonDto(person);        personService.insert(personDto);    }

这么简单的逻辑按理说不会出幺蛾子啊,我先排查了数据库里person id=1的记录发现值是有的啊,然后又排查了我的insert方法,也是没问题的。


经过一段时间的排查,才发现是

PersonDto personDto = PersonMapper.INSTANCE.personToPersonDto(person);

这行代码的问题。证据截图如下:

image.png

前面的时候addTeacherNum还有值,转化后怎么又没值了呢?

大家看到这里肯定猜测是不是我属性名不对,或者属性类型不对。我甚至还删除了之后用复制的方式来保证没有手敲敲错的情况。

image.png

完全是一模一样的属性啊。

image.png

我们知道mapstruct是编译时通过我们的PersonMapper接口来实现实现类,实现类里是setter、getter方法来实现的。于是我打开了PersonMapper的实现类准备一探究竟:

public class PersonMapperImpl implements PersonMapper {    public PersonMapperImpl() {    }    public PersonDto personToPersonDto(Person person) {        if (person == null) {            return null;        } else {            PersonDtoBuilder personDto = PersonDto.builder();            personDto.name(person.getName());            return personDto.build();        }    }}

竟然没有对我这个属性addTeacherNum进行赋值。这让我百思不得其解。只能去看看源码,试图找出原因。

如何调试Maven插件


前面我们提到mapstruct是在代码编译的时候就开始生成代码了,于是我们需要对maven编译期进行调试。方法如下:

  1. maven debug命令
mvnDebug clean compile
  1. idea远程debug

image.png

新建一个remote,然后修改端口为8000,然后在执行maven命令的同时,启动这个remote即可。

源码解析


断点应该打在哪里呢?

我们查看mapstruct的结构,一般先从配置的文件入手
image.png

找到了这个MappingProcessor类,我们可以看到这里面有个process方法,里面又调用了如下的这个方法:

private void processMapperTypeElement(ProcessorContext context, TypeElement mapperTypeElement) {    Object model = null;    for ( ModelElementProcessor<?, ?> processor : getProcessors() ) {        try {            model = process( context, processor, mapperTypeElement, model );        }        catch ( AnnotationProcessingException e ) {           //省略        }    }}

这段代码其实就是调用getProcessors()方法拿到多个processor,然后遍历调用。而这个getProcessors()就是从配置文件里通过SPI的方式加载对象。

image.png

这里面我们重点关注这个Processor:MapperCreationProcessor。它的process方法如下:

@Overridepublic Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<SourceMethod> sourceModel) {    this.elementUtils = context.getElementUtils();    this.typeUtils = context.getTypeUtils();    this.messager =        new MapperAnnotatedFormattingMessenger( context.getMessager(), mapperTypeElement, context.getTypeUtils() );    this.options = context.getOptions();    this.versionInformation = context.getVersionInformation();    this.typeFactory = context.getTypeFactory();    this.accessorNaming = context.getAccessorNaming();    MapperOptions mapperOptions = MapperOptions.getInstanceOn( mapperTypeElement, context.getOptions() );    List<MapperReference> mapperReferences = initReferencedMappers( mapperTypeElement, mapperOptions );    MappingBuilderContext ctx = new MappingBuilderContext(        typeFactory,        elementUtils,        typeUtils,        messager,        accessorNaming,        context.getEnumMappingStrategy(),        context.getEnumTransformationStrategies(),        options,        new MappingResolverImpl(            messager,            elementUtils,            typeUtils,            typeFactory,            new ArrayList<>( sourceModel ),            mapperReferences,            options.isVerbose()        ),        mapperTypeElement,        //sourceModel is passed only to fetch the after/before mapping methods in lifecycleCallbackFactory;        //Consider removing those methods directly into MappingBuilderContext.        Collections.unmodifiableList( sourceModel ),        mapperReferences    );    this.mappingContext = ctx;    return getMapper( mapperTypeElement, mapperOptions, sourceModel );}

getMapper里面有一段这个方法引起我的注意:

List<MappingMethod> mappingMethods = getMappingMethods( mapperOptions, methods );

猜测这段就是获取要写入的set、get方法。
于是一路跟踪:

发现mapstruct里面把方法分为了下面四类,而我的addTeacherNum属性通过lombok生成的方法methodType被分到了ADDER里面。image.png

而在生成我的Mapper实现类的时候它会只过滤setter方法。

List<Accessor> candidates = new ArrayList<>( getSetters() );

至此真相大白。

posted @ 2022-07-12 13:36 女友在高考 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员