美文网首页
MapStruct最佳实践

MapStruct最佳实践

作者: iakuil | 来源:发表于2023-11-21 17:19 被阅读0次

    本文将以一个中级Java开发者角度,探讨如何整合Spring Conversion Service、MapStruct Spring Extensions和Lombok三个常用框架,以最少的代码和配置,完成复杂对象转换工作。

    👉你将学会:

    • 如何将对象转换逻辑从接口代码剥离;

    • 如何灵活地注册Spring对象转换器;

    • 如何使用MapStruct作为转换器的底层实现;

    • 如何使用自己的BeanUtil实现兜底策略。

    版本限制

    • Java 17
    • SpringBoot 3.1.x
    • Lombok 1.18.x
    • MapStruct 1.5.5 Final

    Maven依赖配置

    Lombok和MapStruct都是非常优秀的类库,能够极大提升开发效率,两者实现原理差不多,都是编译期间生成代码。
    MapStruct Spring Extensions是MapStruct基于Spring框架的扩展,自动将Mapper注册为Converter。

      <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.30</version>
                <scope>provided</scope>
            </dependency>
    
            <dependency>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct</artifactId>
                <version>1.5.5.Final</version>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok-mapstruct-binding</artifactId>
                <version>0.2.0</version>
            </dependency>
    
            <dependency>
                <groupId>org.mapstruct.extensions.spring</groupId>
                <artifactId>mapstruct-spring-annotations</artifactId>
                <version>1.1.0</version>
            </dependency>
      </dependencies>
    

    Maven插件配置

     <build>
            <pluginManagement>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <version>3.11.0</version>
                        <configuration>
                            <annotationProcessorPaths>
                                <path>
                                    <groupId>org.projectlombok</groupId>
                                    <artifactId>lombok</artifactId>
                                    <version>1.18.30</version>
                                </path>
                                <path>
                                    <groupId>org.mapstruct</groupId>
                                    <artifactId>mapstruct-processor</artifactId>
                                    <version>1.5.5.Final</version>
                                </path>
                                <path>
                                    <groupId>org.projectlombok</groupId>
                                    <artifactId>lombok-mapstruct-binding</artifactId>
                                    <version>0.2.0</version>
                                </path>
                                <path>
                                    <groupId>org.mapstruct.extensions.spring</groupId>
                                    <artifactId>mapstruct-spring-extensions</artifactId>
                                    <version>1.1.0</version>
                                </path>
                            </annotationProcessorPaths>
                        </configuration>
                    </plugin>
                </plugins>
            </pluginManagement>
        </build>
    

    MapStruct配置

    @MapperConfig注解可以配置一些对象转换过程中的公用配置,比如忽略目标对象中不存在的属性,需要做如下配置:

    @MapperConfig(
        componentModel = "spring",
        unmappedTargetPolicy = ReportingPolicy.IGNORE
    )
    public interface DefaultConverterConfig {
    }
    

    对象转换器

    以下是一个常见的数据字典转换器,实现了Entity/DTO互相转换,并自动注册为Spring Converter:

    @Mapper(config = DefaultConverterConfig.class)
    public interface DictDataToDictDataDtoConverter extends Converter<DictData, DictDataDto> {
    
        @InheritInverseConfiguration
        @DelegatingConverter
        DictData invertConvert(DictDataDto dto);
    
        @AfterMapping
        default void postMapping(DictData entity, @MappingTarget DictDataDto dto) {
            // do something
        }
    
        @AfterMapping
        default void postMapping(DictDataDto dto, @MappingTarget DictData entity) {
            // do something
        }
    }
    

    执行编译命令后会自动生成DictDataToDictDataDtoConverter.class和反向的DictDataDtoToDictDataConverter.class
    当然了,你也可以根据需要自行添加XxxToXxxVoConverter或者XxxToXxxBoConverter等各种转换器。

    转换方法

        @Autowired
        private ConversionService conversionService;
    
        protected <T> T convert(Object source, Class<T> targetType) {
            // 如果没有配置转换器则使用BeanUtil兜底
            return conversionService.canConvert(source.getClass(), targetType) ? conversionService.convert(source, targetType) : BeanUtil.copyProperties((source, targetType);
        }
    
        @SuppressWarnings("unchecked")
        protected <T> List<T> convertAll(@Nullable List<?> sourceList, Class<T> targetType) {
            return conversionService.canConvert(sourceList.get(0).getClass(), targetType)
                ? (List<T>) conversionService.convert(sourceList, TypeDescriptor.forObject(sourceList), TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(targetType)))
                : BeanUtil.copyToList(sourceList, targetType);
        }
    

    以上两个方法是通用的,可以根据需要,放到BaseController或者BaseService里面。
    注意:BeanUtil来自Hutool,也可以替换成自己的工具类。

    接口实例

    如此一来,我们便可以愉快的调用convert()方法,而不用关心目标对象到底是个什么O了🙂

    @RestController
    @RequestMapping("/dict")
    public class DictDataController extends BaseController {
        @Autowired
        private DictDataService dictDataService;
    
        @GetMapping
        public Result<DictDataDto> doQuery(@RequestParam Long id) {
            DictData entity = dictDataService.getById(id);
            return Result.success(convert(entity, DictDataDto.class));
        }
    }
    

    --- THE END ---

    相关文章

      网友评论

          本文标题:MapStruct最佳实践

          本文链接:https://www.haomeiwen.com/subject/drobwdtx.html