49

我需要在生成的映射器实现中注入一个spring服务类,这样我就可以通过

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

这适用于 Mapstruct-1.0 吗?

4

5 回答 5

37

正如 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);

}
于 2017-04-11T10:30:17.727 回答
28

如果您将 Spring 声明为组件模型并添加对以下类型的引用,则应该是可能的myservice

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

该机制旨在提供对生成代码调用的其他映射方法的访问,但您也应该能够以这种方式在表达式中使用它们。只需确保在服务引用中使用生成字段的正确名称即可。

于 2016-08-09T06:43:48.630 回答
22

从 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() ) );
   }
 }

服务可以作为上下文传递。

一个更好的解决方案是使用一个@Context包装MyService而不是直接传递的类MyService@AfterMapping可以在这个“上下文”类上实现一个方法:void map( @MappingTarget Target.X target, Source.ID source )保持映射逻辑与查找逻辑无关。在 MapStruct示例存储库中查看此示例。

于 2018-07-11T19:27:00.813 回答
17

我正在使用 Mapstruct 1.3.1,我发现使用装饰器很容易解决这个问题。

例子:

@Mapper(unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
 componentModel = "spring")
@DecoratedWith(FooMapperDecorator.class)
public interface FooMapper {

    FooDTO map(Foo foo);
}
public abstract class FooMapperDecorator implements FooMapper{

    @Autowired
    @Qualifier("delegate")
    private FooMapper delegate;

    @Autowired
    private MyBean myBean;

    @Override
    public FooDTO map(Foo foo) {

        FooDTO fooDTO = delegate.map(foo);

        fooDTO.setBar(myBean.getBar(foo.getBarId());

        return fooDTO;
    }
}

Mapstruct 将生成 2 个类并将扩展 FooMapperDecorator 的 FooMapper 标记为 @Primary bean。

于 2020-02-13T21:51:48.653 回答
16

除了上面的答案之外,值得补充的是,在 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);

}
于 2018-11-22T11:28:11.593 回答