Mapstruct - 如何在 Generated Mapper 类中注入 spring 依赖项
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38807415/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Mapstruct - How can I inject a spring dependency in the Generated Mapper class
提问by Karim Tawfik
I need to inject a spring service class in the generated mapper implementation, so that I can use it via
我需要在生成的映射器实现中注入一个 spring 服务类,以便我可以通过
@Mapping(target="x", expression="java(myservice.findById(id))")"
Is this applicable in Mapstruct-1.0?
这适用于 Mapstruct-1.0 吗?
采纳答案by Gunnar
It should be possible if you declare Spring as the component model and add a reference to the type of myservice
:
如果您将 Spring 声明为组件模型并添加对以下类型的引用,则应该是可能的myservice
:
@Mapper(componentModel="spring", uses=MyService.class)
public interface MyMapper { ... }
That mechanism is meant for providing access to other mapping methods to be called by generated code, but you should be able to use them in the expression that way, too. Just make sure you use the correct name of the generated field with the service reference.
该机制旨在提供对由生成的代码调用的其他映射方法的访问,但您也应该能够以这种方式在表达式中使用它们。只需确保将生成的字段的正确名称与服务引用一起使用。
回答by Bob
As commented by brettanomyces, the service won't be injected if it is not used in mapping operations other than expressions.
正如 brettanomyces 评论的那样,如果该服务不用于表达式以外的映射操作,则不会注入该服务。
The only way I found to this is :
我发现的唯一方法是:
- Transform my mapper interface into an abstract class
- Inject the service in the abstract class
- Make it protected so the "implementation" of the abstract class has access
- 将我的映射器接口转换为抽象类
- 在抽象类中注入服务
- 使其受保护,以便抽象类的“实现”可以访问
I'm using CDI but it should be the samel with Spring :
我正在使用 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);
}
回答by Sjaak
Since 1.2 this can be solved with a combination of @AfterMapping and @Context.. Like this:
从 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() ) );
}
}
The service can be passed as context.
该服务可以作为上下文传递。
A nicer solution would be to use an @Context
class which wrap MyService
in stead of passing MyService
directly. An @AfterMapping
method can be implemented on this "context" class: void map( @MappingTarget Target.X target, Source.ID source )
keeping the mapping logic clear of lookup logic. Checkout this example in the MapStruct example repository.
更好的解决方案是使用@Context
包装MyService
而不是MyService
直接传递的类。@AfterMapping
可以在这个“上下文”类上实现一种方法:void map( @MappingTarget Target.X target, Source.ID source )
使映射逻辑与查找逻辑保持清晰。在 MapStruct示例存储库中查看此示例。
回答by Cmyker
What's worth to add in addition to the answers above is that there is more clean way to use spring service in mapstruct mapper, that fits more into "separation of concerns" design concept, called "qualifier". For sake of simplicity I prefer named qualifier as noted here http://mapstruct.org/documentation/stable/reference/html/#selection-based-on-qualifiersExample would be:
除了上面的答案之外,值得补充的是,在 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);
}
}
This is how you use it in your mapper:
这是您在映射器中使用它的方式:
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);
}
回答by Jim Cox
I am using Mapstruct 1.3.1 and I have found this problem is easy to solve using a decorator.
我使用的是 Mapstruct 1.3.1,我发现使用装饰器很容易解决这个问题。
Example:
例子:
@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 will generate 2 classes and mark the FooMapper that extends FooMapperDecorator as the @Primary bean.
Mapstruct 将生成 2 个类并将扩展 FooMapperDecorator 的 FooMapper 标记为@Primary bean。