我可以扩展 MapStruct 方法吗?

Can I extend MapStruct methods?

我正在开发一个库,我希望该库有一个像这样的映射器:

import org.mapstruct.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import spring.graphql.rest.rql.core.nodes.PropertyNode;
import spring.graphql.rest.rql.core.nodes.TraversalMapper;
import spring.graphql.rest.rql.example.dto.AccountDto;
import spring.graphql.rest.rql.example.models.Account;

import java.util.*;

@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
        nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE, uses = {TraversalMapper.class})
public abstract class AccountMapper {

    @Autowired
    @Qualifier("accountMapperImpl")
    protected AccountMapper accountMapper;

    @Autowired
    protected PostMapper postMapper;

    @Autowired
    protected PersonMapper personMapper;

    @Autowired
    protected CommentMapper commentMapper;

    @IterableMapping(qualifiedByName = "dynamicAccounts")
    public abstract Set<AccountDto> rqlToAccountDtos(Set<Account> entity, @Context StringBuilder currentPath, @Context List<PropertyNode> propertyNodes, @Context List<String> properties, @Context String property);

    // TODO: Implement automatic generation/addition of these mappers
    @Named("dynamicAccounts")
    @Mapping(target = "friends", expression = "java(properties.contains(\"friends\") ? accountMapper.rqlToAccountDtos(entity.getFriends(), currentPath, propertyNodes, properties, \"friends\") : null)")
    @Mapping(target = "posts", expression = "java(properties.contains(\"posts\") ? postMapper.toPostDtos(entity.getPosts(), currentPath, propertyNodes, properties, \"posts\") : null)")
    @Mapping(target = "comments", expression = "java(properties.contains(\"comments\") ? commentMapper.toCommentDtos(entity.getComments(), currentPath, propertyNodes, properties, \"comments\") : null)")
    @Mapping(target = "person", expression = "java(properties.contains(\"person\") ? personMapper.toPersonDto(entity.getPerson(), currentPath, propertyNodes, properties, \"person\") : null)")
    public abstract AccountDto rqlToAccountDto(Account entity, @Context StringBuilder currentPath, @Context List<PropertyNode> propertyNodes, @Context List<String> properties, @Context String property);

    @Named("dynamicAccountsDefaultCollection")
    public Set<AccountDto> toAccountDtosDefault(Collection<Account> entity, @Context List<PropertyNode> propertyNodes) {
        return rqlToAccountDtos(new HashSet<>(entity), new StringBuilder(), propertyNodes, new ArrayList<>(), "");
    }

    @Named("dynamicAccountsDefault")
    public AccountDto toAccountDtoDefault(Account entity, @Context List<PropertyNode> propertyNodes) {
        return rqlToAccountDto(entity, new StringBuilder(), propertyNodes, new ArrayList<>(), "");
    }
}

用于内部重新映射。现在,我想添加一个用户可以“覆盖”此方法的功能,例如,他们可以添加一条语句来忽略帐户密码,同时还调用内部方法。

举几个例子作为参考:

import org.mapstruct.*;
import spring.graphql.rest.rql.core.nodes.PropertyNode;
import spring.graphql.rest.rql.example.dto.AccountDto;
import spring.graphql.rest.rql.example.models.Account;

import java.util.List;

@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
        nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public abstract class RealAccountMapper extends AccountMapper {

    @Mapping(target = "username", ignore = true)
    public abstract AccountDto toAccountDtoDefault(Account entity, @Context List<PropertyNode> propertyNodes);
}

@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
        nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public abstract class RealAccountMapper {

    @Mapping(target = "username", ignore = true)
    @Mapper(uses = AccountMapper.class, qualifiedByName = "dynamicAccountsDefault")
    public abstract AccountDto toAccountDto(Account entity, @Context List<PropertyNode> propertyNodes);
}

用户应该能够定义一个 属性 来忽略,或者例如添加指定的来源。根据我的实验,即使没有覆盖等,它也只是生成一个单独的映射方法,而不是尝试重用另一个。

这是否可能不需要在用户端实现调用内部方法的自定义方法?如果不是,是否有特殊原因,或者它只是没有作为 MapStruct 中可能的功能出现?

MapStruct 在编译期间生成代码。这意味着不可能忽略某些属性并为此调用另一个映射器,因为另一个映射器已经实现了。

作为一项新功能,我们可以向 @Mapper 添加一些内容,这基本上会合并来自父映射器的配置。

例如

@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
        nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE, methodOverrideStrategy = OverrideStrategy.MERGE)
public abstract class RealAccountMapper extends AccountMapper {

    @Override
    @Mapping(target = "username", ignore = true)
    public abstract AccountDto toAccountDtoDefault(Account entity, @Context List<PropertyNode> propertyNodes);
}

这意味着如果这个新的 methodOverrideStrategy(名称不是最终名称)将意味着 MapStruct 将在 toAccountDtoDefault 方法上执行注释合并。

对于 MapStruct,就好像用户已经写了一样。

    @Named("dynamicAccountsDefault")
    @Override
    @Mapping(target = "username", ignore = true)
    public abstract AccountDto toAccountDtoDefault(Account entity, @Context List<PropertyNode> propertyNodes);

我建议您在 MapStruct issue tracker 中将此作为功能请求提出。