web-dev-qa-db-ja.com

Mapstruct-生成されたマッパークラスにスプリングの依存関係を挿入する方法

生成されたマッパー実装にスプリングサービスクラスを挿入する必要があります。

   @Mapping(target="x", expression="Java(myservice.findById(id))")"

これはMapstruct-1.0に適用可能ですか?

13
Karim Tawfik

Springをコンポーネントモデルとして宣言し、myserviceの型への参照を追加すると可能になります。

@Mapper(componentModel="spring", uses=MyService.class)
public interface MyMapper { ... }

このメカニズムは、生成されたコードによって呼び出される他のマッピングメソッドへのアクセスを提供することを目的としていますが、その方法でも式で使用できるはずです。サービス参照で生成されたフィールドの正しい名前を使用していることを確認してください。

12
Gunnar

Brettanomycesがコメントしているように、式以外のマッピング操作で使用されない場合、サービスは注入されません。

私がこれを見つけた唯一の方法は:

  • マッパーインターフェイスを抽象クラスに変換する
  • 抽象クラスにサービスを注入します
  • 抽象クラスの「実装」がアクセスできるように保護します

私はCDIを使用していますが、Springと同じでなければなりません:

@Mapper(
        unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
        componentModel = "spring",
        uses = {
            // My other mappers...
        })
public abstract class MyMapper {

    @Autowired
    protected MyService myService;

    @Mappings({
        @Mapping(target="x", expression="Java(myservice.findById(obj.getId())))")
    })
    public abstract Dto myMappingMethod(Object obj);

}
18
Bob

1.2以降、これは@AfterMappingと@Contextの組み合わせで解決できます。このように:

@Mapper(componentModel="spring")
public interface MyMapper { 

   @Mapping(target="x",ignore = true)
   // other mappings
   Target map( Source source, @Context MyService service);

   @AfterMapping
   default void map( @MappingTarget Target.X target, Source.ID source, @Context MyService service) {
        target.set( service.findById( source.getId() ) );
   }
 }

サービスはコンテキストとして渡すことができます。

より良い解決策は、MyServiceを直接渡す代わりに、MyServiceをラップする@Contextクラスを使用することです。 @AfterMappingメソッドは、この「コンテキスト」クラスに実装できます。void map( @MappingTarget Target.X target, Source.ID source )マッピングロジックをルックアップロジックから解放します。 MapStruct example repository でこの例を確認してください。

10
Sjaak

上記の回答に加えて追加する価値があるのは、mapstruct mapperでspringサービスを使用するよりクリーンな方法があることです。これは、「修飾子」と呼ばれる「関心の分離」設計コンセプトにより適合します。簡単にするために、ここに記載されている名前付き修飾子を好みます http://mapstruct.org/documentation/stable/reference/html/#selection-based-on-qualifiers 例は次のようになります:

import org.mapstruct.Named;
import org.springframework.stereotype.Component;

@Component
public class EventTimeQualifier {

    private EventTimeFactory eventTimeFactory; // ---> this is the service you want yo use

    public EventTimeQualifier(EventTimeFactory eventTimeFactory) {
        this.eventTimeFactory = eventTimeFactory;
    }

    @Named("stringToEventTime")
    public EventTime stringToEventTime(String time) {
        return eventTimeFactory.fromString(time);
    }

}

これは、マッパーでの使用方法です。

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(componentModel = "spring", uses = EventTimeQualifier.class)
public interface EventMapper {

    @Mapping(source = "checkpointTime", target = "eventTime", qualifiedByName = "stringToEventTime")
    Event map(EventDTO eventDTO);

}
2
Cmyker