J'ai besoin d'injecter une classe de service de ressort dans l'implémentation de mappeur générée, afin de pouvoir l'utiliser via
@Mapping(target="x", expression="Java(myservice.findById(id))")"
Est-ce applicable dans Mapstruct-1.0?
Cela devrait être possible si vous déclarez Spring en tant que modèle de composant et ajoutez une référence au type myservice
:
@Mapper(componentModel="spring", uses=MyService.class)
public interface MyMapper { ... }
Ce mécanisme est destiné à fournir un accès à d'autres méthodes de mappage appelées par le code généré, mais vous devriez également pouvoir les utiliser dans l'expression. Assurez-vous simplement que vous utilisez le nom correct du champ généré avec la référence de service.
Comme l'a commenté brettanomyces, le service ne sera pas injecté s'il n'est pas utilisé dans des opérations de mappage autres que des expressions.
Le seul moyen que j'ai trouvé pour cela est:
J'utilise CDI mais ce devrait être le samel avec 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);
}
Depuis 1.2, cela peut être résolu avec une combinaison de @AfterMapping et @Context .. Comme ceci:
@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() ) );
}
}
Le service peut être passé en tant que contexte.
Une solution plus intéressante consisterait à utiliser une classe @Context
qui encapsule MyService
au lieu de passer directement MyService
. Une méthode @AfterMapping
peut être implémentée sur cette classe "context": void map( @MappingTarget Target.X target, Source.ID source )
en gardant la logique de mappage à l'écart de la logique de recherche. Consultez cet exemple dans le référentiel MapStruct example .
Outre les réponses ci-dessus, il est intéressant d’ajouter qu’il existe un moyen plus propre d’utiliser le service Spring dans Mapstruct Mapper, qui correspond davantage au concept de conception "de séparation des préoccupations", appelé "qualificateur". simplicité je préfère le qualificatif nommé, comme indiqué ici http://mapstruct.org/documentation/stable/reference/html/#selection-based-on-qualifiers Exemple:
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);
}
}
Voici comment vous l'utilisez dans votre mappeur:
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);
}