Mapstruct - 如何在Generated Mapper类中注入spring依赖项

Kar*_*fik 16 spring mapstruct

我需要在生成的mapper实现中注入一个spring服务类,以便我可以通过它来使用它

   @Mapping(target="x", expression="java(myservice.findById(id))")"
Run Code Online (Sandbox Code Playgroud)

这适用于Mapstruct-1.0吗?

Bob*_*Bob 20

正如brettanomyces所评论的那样,如果不在表达式之外的映射操作中使用该服务,则不会注入该服务.

我发现这一点的唯一方法是:

  • 将我的mapper接口转换为抽象类
  • 在抽象类中注入服务
  • 使其受到保护,以便抽象类的"实现"具有访问权限

我正在使用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);

}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢。糟糕的是你不能使用构造函数注入。 (7认同)
  • 这可能是显而易见的,但它困扰了我几分钟。为了使其在 Spring 中工作,您还必须将映射器注入到您的服务中,而不是使用 INSTANCE 映射器类。 (2认同)

Cmy*_*ker 16

除了上面的答案之外,值得补充的是,在 mapstruct 映射器中有更干净的使用 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);
    }

}
Run Code Online (Sandbox Code Playgroud)

这是您在映射器中使用它的方式:

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

}
Run Code Online (Sandbox Code Playgroud)

  • “EventTimeQualifier”处的“@Mapper”注释似乎没有必要。由于 `componentModel="spring"`,它在没有它的情况下对我有用。 (2认同)

Jim*_*Cox 15

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

例子:

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

    FooDTO map(Foo foo);
}
Run Code Online (Sandbox Code Playgroud)
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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这是我认为最好的答案 (2认同)

Gun*_*nar 13

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

@Mapper(componentModel="spring", uses=MyService.class)
public interface MyMapper { ... }
Run Code Online (Sandbox Code Playgroud)

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

  • 我有同样的问题,似乎用'uses'声明的类只有在其中一个方法被用作source-> target映射时才会被自动装配,所以如果它们的唯一用途是在表达式中它们将*不*得到自动装配Autowired (3认同)
  • 啊,如果你*只想*在表达式中使用它,这是一个有趣的观点.你可以在[我们的跟踪器](https://github.com/mapstruct/mapstruct/issues)中打开一个问题吗?谢谢! (3认同)
  • 请注意:这个 `uses` 对我来说适用于像 ```@Mapping(target="x", source="id")``` 这样的映射,而不是 ```@Mapping(target="x",表达式=“java(myservice.findById(id))”)“``` (2认同)

Sja*_*aak 13

从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() ) );
   }
 }
Run Code Online (Sandbox Code Playgroud)

该服务可以作为上下文传递.

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

  • 不适用于 mapstruct 1.3.1.Final。带有“@Context”MyMapper myMapper 的“@AferMapping”方法已被忽略 (2认同)
  • 在不知道什么不起作用的情况下回答“它不起作用”有点困难。请查看链接中提供的示例。它在 1.4.1.Final 中仍然有效。如果 MapStruct 存在问题,请提出问题。 (2认同)