Mapstructにバグが見つかりました

序文


要件の開発プロセスでは、オブジェクトがデータベースに挿入されると、フィールドは常に空であることがわかります。簡略化されたコードは次のとおりです。

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

このような単純なロジックでは蛾は発生しないと言っても過言ではありません。まずデータベースのpersonid= 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デバッグコマンド
mvnDebug clean compile
复制代码
  1. アイデアリモートデバッグ

image.png

新しいリモートを作成し、ポートを8000に変更してから、mavenコマンドの実行中にリモートを起動します。

ソースコード分析


ブレークポイントはどこにヒットする必要がありますか?

mapstructの構造を見ていきます。通常は、構成ファイルから始めます。image.png

このMappingProcessorクラスを見つけると、次のメソッドを呼び出すプロセスメソッドがあることがわかります。

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()メソッドを呼び出して複数のプロセッサを取得し、呼び出しをトラバースしています。そして、このgetProcessors()は、SPIを介して構成ファイルからオブジェクトをロードします。

image.png

ここでは、このProcessor:MapperCreationProcessorに焦点を当てます。その処理方法は次のとおりです。

@Override
public 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 );
复制代码

このセクションは、セットを取得し、メソッドを作成することだと思います。だから従う:

mapstructのメソッドは、次の4つのカテゴリに分類され、私のaddTeacherNumプロパティは、lombokによって生成されたメソッドmethodTypeを介してADDERに分割されていることがわかります。image.png

また、Mapper実装クラスを生成すると、setterメソッドのみがフィルタリングされます。

List<Accessor> candidates = new ArrayList<>( getSetters() );
复制代码

これまでのところ真実が出てきました。

ナゲッツテクノロジーコミュニティのクリエイター署名プログラムの募集に参加しています。リンクをクリックして登録し、送信してください。

おすすめ

転載: juejin.im/post/7119351475649642526