You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何MapStruct的@BeforeMapping未返回修改后的列表?

问题

我尝试用MapStruct把UserDTO列表里的非空元素映射成User列表。虽然单个的toEntity(UserDTO userDTO)方法会校验空值,但集合映射生成的列表还是保留了原DTO列表的长度(可能包含空元素)。于是我写了下面的@BeforeMapping方法:

@BeforeMapping
default void filterNonNullDTO(List<UserDTO> userDTO){
    userDTO=userDTO.stream().filter(Objects::nonNull).collect(Collectors.toList());
}

但MapStruct生成的实现代码是这样的:

@Override
public List<User> toEntities(List<UserDTO> userDtos) {
    filterNonNullDTO( userDtos );

    if ( userDtos == null ) {
        return null;
    }

    List<User> list = new ArrayList<User>( userDtos.size() );
    for ( UserDTO userDTO : userDtos ) {
        list.add( toEntity( userDTO ) );
    }

    return list;
}

我写了JUnit5测试用例验证逻辑,但没通过:预期列表长度为1,实际是2。测试代码如下:

@Test
void mapUserDTOsToUserShouldReturnNonNullUsers(){
    List<AdminUserDTO> userDTOs= new ArrayList<>();
    userDTOs.add(userDto);
    userDTOs.add(null);
    List<User> users= userMapper.toEntities(userDTOs);
    assertThat(users).isNotEmpty().size().isEqualTo(1);
}

测试结果:expected: 1 but was: 2,请问我的代码哪里有问题?


问题根源

你的filterNonNullDTO方法犯了Java参数传递的典型错误:Java是值传递,你在方法里重新赋值userDTO变量,只是改变了方法内部的局部变量引用,并不会修改原列表的内容或者外部传入的userDtos引用。

生成的代码里调用filterNonNullDTO(userDtos)后,原userDtos列表根本没被修改,还是原来包含一个非空元素和一个null的列表,所以后续遍历的时候依然会循环2次,最终生成长度为2的User列表(其中一个是toEntity(null)返回的null)。


解决方案

你需要直接修改传入的列表,而不是重新赋值局部变量。可以用removeIf方法直接移除列表中的null元素:

@BeforeMapping
default void filterNonNullDTO(List<UserDTO> userDTOs){
    if (userDTOs != null) {
        userDTOs.removeIf(Objects::isNull);
    }
}

这样调用这个方法后,原列表里的null元素会被直接移除,后续MapStruct遍历的时候就只会处理剩下的非空元素,最终生成长度符合预期的User列表。

内容的提问来源于stack exchange,提问作者Islam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 17:33:11