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

MapStruct自定义命名策略实现前缀字段自动映射遇null问题

MapStruct自定义命名策略实现带前缀实体与DTO自动映射问题排查

问题背景

实体类字段带固定前缀(如UserEntity的usrName、usrEmail),DTO类对应字段无前缀(name、email),希望通过MapStruct全局自定义命名策略替代@Mapping注解实现自动映射,但配置后DTO所有字段均为null,与预期不符。

已实现配置

1. Maven pom.xml配置片段

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <source>17</source>
                <target>17</target>
                <annotationProcessorPaths>
                    <!-- MapStruct Processor -->
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>1.4.1.Final</version>
                    </path>
                    <!-- Lombok -->
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>1.18.28</version>
                    </path>
                </annotationProcessorPaths>
                <compilerArgs>
                    <arg>-Amapstruct.accessorNamingStrategy=com.mypackage.CustomAccessorNamingStrategy</arg>
                </compilerArgs>
            </configuration>
        </plugin>
    </plugins>
</build>

2. 自定义命名策略类

import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy;
import javax.lang.model.element.ExecutableElement;
import org.mapstruct.ap.internal.util.IntrospectorUtils;

public class CustomAccessorNamingStrategy extends DefaultAccessorNamingStrategy {

    @Override
    public String getPropertyName(ExecutableElement getterOrSetterMethod) {
        String originalPropertyName = super.getPropertyName(getterOrSetterMethod);

        if (originalPropertyName.startsWith("usr")) {
            return IntrospectorUtils.decapitalize(originalPropertyName.substring(3));
        }

        return originalPropertyName; 
    }
}

3. Mapper接口与测试代码

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface UserMapper {
    
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    UserDTO toDTO(UserEntity userEntity);
}
import org.springframework.stereotype.Service;

@Service
public class UserService {

    public void exampleUsage() {
        UserEntity userEntity = new UserEntity();
        userEntity.setUsrName("John Doe");
        userEntity.setUsrEmail("john@example.com");
        userEntity.setUsrAddress("123 Main St");
        userEntity.setUsrAge("30");

        UserDTO userDTO = UserMapper.INSTANCE.toDTO(userEntity);
        System.out.println("Mapped to DTO: " + userDTO);
    }
}

预期结果:Mapped to DTO: UserDTO{name='John Doe', email='john@example.com', address='123 Main St', age='30'}
实际结果:Mapped to DTO: UserDTO{name=null, email=null, address=null, age=null}

问题原因排查

  1. Lombok与MapStruct协同问题:MapStruct无法正确识别Lombok生成的getter/setter方法,导致无法读取实体类字段值。
  2. 自定义策略类加载失败:编译参数中指定的策略类包路径错误,或MapStruct 1.4.1.Final版本对SPI支持存在兼容性问题。
  3. 编译顺序问题:自定义策略类未在MapStruct注解处理器执行前完成编译,导致处理器无法加载该类。

解决方法

1. 完善编译参数,确保Lombok与MapStruct兼容

在compilerArgs中添加Lombok支持参数,同时开启调试日志便于排查:

<compilerArgs>
    <arg>-Amapstruct.accessorNamingStrategy=com.mypackage.CustomAccessorNamingStrategy</arg>
    <arg>-Amapstruct.lombok.addon=true</arg>
    <arg>-Amapstruct.verbose=true</arg>
</compilerArgs>

2. 验证并修正策略类包路径

确认-Amapstruct.accessorNamingStrategy指定的类全限定名与实际代码完全一致,比如策略类在com.example.mapper下,则参数应为com.example.mapper.CustomAccessorNamingStrategy。

3. 升级MapStruct至稳定版本

将MapStruct版本升级到1.5.5.Final或更高版本,新版本对SPI实现和Lombok兼容性更优:

<path>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.5.Final</version>
</path>

4. 调试自定义策略执行逻辑

在策略类中添加日志打印,确认属性转换逻辑是否执行:

import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy;
import javax.lang.model.element.ExecutableElement;
import org.mapstruct.ap.internal.util.IntrospectorUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CustomAccessorNamingStrategy extends DefaultAccessorNamingStrategy {

    private static final Logger logger = LoggerFactory.getLogger(CustomAccessorNamingStrategy.class);

    @Override
    public String getPropertyName(ExecutableElement getterOrSetterMethod) {
        String originalPropertyName = super.getPropertyName(getterOrSetterMethod);
        logger.debug("Original property name from getter/setter: {}", originalPropertyName);

        if (originalPropertyName.startsWith("usr")) {
            String convertedName = IntrospectorUtils.decapitalize(originalPropertyName.substring(3));
            logger.debug("Converted property name: {}", convertedName);
            return convertedName;
        }

        return originalPropertyName;
    }
}

5. 重新编译并验证

执行mvn clean compile查看编译日志,确认自定义策略类已加载且属性转换逻辑正常执行,再运行测试代码验证映射结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 13:28:10