Springboot & mapstruct 自动装配循环依赖问题

问题描述 投票:0回答:1

我有一个 springboot 应用程序,它依赖于许多(mapstruct)映射器 bean。在许多情况下,一个映射器以双向方式依赖另一个映射器(从而形成一个循环)。

Spring 不喜欢这样并抱怨。


应用程序无法启动


描述:

应用程序上下文中的一些bean的依赖关系形成一个循环:

┌──────┐ | AssociatedAccountMapperImpl(字段私有 com.server.springboot.mapping.global.ContactLocationRelationMapper com.server.springboot.mapping.customers.AssociatedAccountMapperImpl.contactLocationRelationMapper) ↑ ↓ | contactLocationRelationMapperImpl (字段私有 com.server.springboot.mapping.customers.AssociatedAccountMapper com.server.springboot.mapping.global.ContactLocationRelationMapperImpl.linkedAccountMapper) └──────┘

行动:

不鼓励依赖循环引用,并且默认情况下禁止它们。更新您的应用程序以消除 Bean 之间的依赖循环。作为最后的手段,可以通过将 spring.main.allow-circular-references 设置为 true 来自动打破循环。

现在我已经按照官方的mapstruct指南text来解决周期性问题,但是我特定的springboot和mapstruct组合问题似乎在互联网上的任何地方都没有记录的答案。

您在他们的回答中看到,他们建议您将

CycleAvoidingMappingContext
对象作为参数传递到您的方法中,然后他们通过以下调用检索映射器
EmployeeMapper MAPPER = Mappers.getMapper( EmployeeMapper.class );

但是,在我使用 springboot 的情况下,我通过 '@Mapper(componentModel = "spring",uses = CycleAvoidingMappingContext.class)` 注释自动装配了

CycleAvoidingMappingContext
对象。

这是发布的 github 解决方案

public class CycleAvoidingMappingContext {
    private Map<Object, Object> knownInstances = new IdentityHashMap<Object, Object>();

    @BeforeMapping
    public <T> T getMappedInstance(Object source, @TargetType Class<T> targetType) {
        return (T) knownInstances.get( source );
    }

    @BeforeMapping
    public void storeMappedInstance(Object source, @MappingTarget Object target) {
        knownInstances.put( source, target );
    }
}

这是接口及其关联的自动创建代码的示例。

@Mapper(
    componentModel = "spring",
    subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION,
    uses = {
        CycleAvoidingMappingContext.class,
        CategoryMapper.class,
        NoteMapper.class, 
        PaymentTermMapper.class,
        PriceLevelMapper.class,
        LateFeeMapper.class,
        ContactLocationRelationMapper.class
    }
)
public interface AssociatedAccountMapper {



 AssociatedAccountDTO toDTO(AssociatedAccount source);
}
@Component
public class AssociatedAccountMapperImpl implements AssociatedAccountMapper {

    @Autowired
    private CycleAvoidingMappingContext cycleAvoidingMappingContext;
    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private NoteMapper noteMapper;
    @Autowired
    private PaymentTermMapper paymentTermMapper;
    @Autowired
    private PriceLevelMapper priceLevelMapper;
    @Autowired
    private LateFeeMapper lateFeeMapper;
    @Autowired
    private ContactLocationRelationMapper contactLocationRelationMapper;

    @Override
    public AssociatedAccountDTO toDTO(AssociatedAccount source) {
        AssociatedAccountDTO target = cycleAvoidingMappingContext.getMappedInstance( source, AssociatedAccountDTO.class );

正如您所看到的

uses = 
注释中的
@Mapper
属性会导致所有类都被自动装配并在必要时由 mapstruct 使用。

这太棒了,但是正如您所看到的,这个类是与我的 contactLocationRelationMapper 自动装配的,而我的 contactLocationRelationMapper 又是与 AssosciatedAccountMapper 自动装配的,从而导致了这种循环依赖问题,尽管该方法使用

CycleAvoidingMappingContext
对象来避免循环.

所以在我看来,问题更多地来自于映射器被自动连接的事实 而不是方法本身使用的上下文导致了这种循环依赖错误。

根据我的研究,我发现有一些我并不是特别喜欢的解决方法。

  1. @Lazy
    注释自动装配的映射器实例,它可以工作,但是它需要我手动将
    @Lazy
    添加到各自映射器实现中循环的所有映射器实例中 - 这种方法很糟糕,因为在任何更改或重新加载之后,mapstruct重建代码,从而删除我手动添加的
    @lazy
    注释,让我一遍又一遍地做同样不必要的工作。

  2. 生成式人工智能建议我可以使用构造函数自动装配,而不是使用字段变量自动装配。我不喜欢这种解决办法,因为它似乎没有必要,而且不起作用。

  3. 我总是可以删除 springboot 正在做的工作并手动实现我的映射器及其 bean,如 github 页面所示,但是我不喜欢这样的事实:我可以访问 springboot 并且无法使用它的强大功能mapstruct 只是因为自动装配会导致循环依赖,而没有其他记录的解决方案。

spring spring-boot autowired cycle mapstruct
1个回答
0
投票

您可以使用版本

1.6.0.Beta1
中提供的 setter 注入,Spring Docs 也建议这样做,如下所示:

@Mapper(
    componentModel = "spring",
    subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION,
    injectionStrategy = InjectionStrategy.SETTER
    uses = {
        CycleAvoidingMappingContext.class,
        CategoryMapper.class,
        NoteMapper.class, 
        PaymentTermMapper.class,
        PriceLevelMapper.class,
        LateFeeMapper.class,
        ContactLocationRelationMapper.class
    }
)
public interface AssociatedAccountMapper {

   AssociatedAccountDTO toDTO(AssociatedAccount source);

}

还有一个非官方的

springlazy
组件模型扩展。有关更多详细信息,请参阅 MapStruct GitHub 问题 #3558

© www.soinside.com 2019 - 2024. All rights reserved.