专栏名称: Java知音
专注于Java,推送技术文章,热门开源项目等。致力打造一个有实用,有情怀的Java技术公众号!
目录
相关文章推荐
重庆之声  ·  今天,送别黄旭华院士! ·  昨天  
重庆之声  ·  今天,送别黄旭华院士! ·  昨天  
宝鸡市场监管  ·  我国牵头!7项国际标准项目立项 ·  2 天前  
宝鸡市场监管  ·  我国牵头!7项国际标准项目立项 ·  2 天前  
酷玩实验室  ·  缺爱的年轻人,涌入ChatGPT ·  4 天前  
芯东西  ·  创历史!DeepSeek获16大国产AI芯片 ... ·  4 天前  
芯东西  ·  创历史!DeepSeek获16大国产AI芯片 ... ·  4 天前  
51好读  ›  专栏  ›  Java知音

MapStruct 超神进阶用法,让你的代码效率提升十倍!

Java知音  · 公众号  · 科技自媒体  · 2024-09-18 19:50

主要观点总结

本文介绍了MapStruct框架的进阶用法,包括expression、qualifiedByName、nullValueMappingStrategy、Decorator等特性的使用。

关键观点总结

关键观点1: MapStruct简介

MapStruct是一个Java编译时注解处理框架,用于将一种Java Bean对象自动映射成另一种类型的对象,具有简洁、性能优秀、安全、灵活和良好的IDE支持等特点。

关键观点2: expression特性

在转化时,可以通过expression执行Java表达式,例如设置createTime字段的值为System.currentTimeMillis()。

关键观点3: qualifiedByName特性

做映射时,默认情况下使用get/set方法,如果需求复杂,可以使用qualifiedByName指定使用其他的处理方法,如将name字段转化为大写格式。

关键观点4: nullValueMappingStrategy策略

当source为null时,对应的target的处理策略可以通过nullValueMappingStrategy设置。默认是RETURN_NULL,也可以设置为RETURN_DEFAULT,如将null的ids字段设置为空list。

关键观点5: Decorator特性

可以通过创建一个Decorator类来修饰方法并实现全局处理,例如对空字段赋予特定的默认值。


正文

前言

MapStruct 是一个 Java 编译时注解处理框架,用来自动化将一种 Java Bean 对象映射成另一种类型的对象。

该框架的主要目标是使开发人员在尽可能少的代码和最低的运行时间成本下实现属性映射。MapStruct 通过在编译时生成代码来实现这点,这与大多数其他 Java Bean 映射框架在运行时通过反射进行映射形成了鲜明对比。

MapStruct 具有以下主要特性:

  • 简洁: 简化了 Java Beans 之间转换的代码,自动生成使用简单的赋值语句完成的映射实现。
  • 性能优秀: 由于 MapStruct 是在编译时生成代码,不涉及任何反射,因此执行映射的性能优越。
  • 安全: 通过在编译时生成映射代码,MapStruct 提供了类型安全的映射,并能在编译时就发现潜在的错误。
  • 灵活: 可通过自定义转换方法、类型转换和映射策略等来满足复杂的映射需求。
  • 良好的 IDE 支持: 由于 MapStruct 是编译时工具,所以拥有良好的 IDE 集成,如代码自动完成、错误高亮等。

总的来说, MapStruct 是一个强大且灵活的映射框架,很好的解决有关对象转换的问题,实现了代码的简洁和性能的兼顾。MapStruct的常规用法,网上有很多教程了,本文将列举一些进阶用法,方便日常开发使用。

expression

在转化的时候,执行 java 表达式,直接看例子:

@Mapper(componentModel = "spring")
public interface MyMapper {
    @Mapping(target = "createTime", expression = "java(System.currentTimeMillis())")
    Target toTarget(Source source);
}

转化成 target 对象时,createTime字段的值,会设置为 System.currentTimeMillis() ,生成的代码如下:

@Component
public class MyMapperImpl implements MyMapper {

    @Override
    public Target toTarget(Source source) {
        Target target = new Target();
        target.setCreateTime( System.currentTimeMillis() );
        return target;
    }
}

qualifiedByName

做映射时,默认情况下,从source 字段到target 字段是直接使用 get/set ,如下:

@Data
public class Source {
    private String name;
}

@Data
public class Target {
    private String name;
}
    

@Mapper(componentModel = "spring")
public interface MyMapper {
    Target toTarget(Source source);
}

生成的转化代码类如下:

@Component
public class MyMapperImpl implements MyMapper {

    @Override
    public Target toTarget(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        // 无脑 set/get
        target.setName( source.getName() );

        return target;
    }
}

如果这种直接的 set/get 无法满足需求,比如需要把 name 转化成大写格式,那么可以使用 qualifiedByName :

@Mapper(componentModel = "spring")
public interface MyMapper {
    @Mapping(target = "name", source = "name", qualifiedByName = "toUpperCase")
    Target toTarget(Source source);

    @Named("toUpperCase")
    default String toUpperCase(String value) {
        // 这里写转换大写的逻辑
        return value == null ? null : value.toUpperCase();
    }
}

生成的代码如下:

@Component
public class MyMapperImpl implements MyMapper {

    @Override
    public Target toTarget(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        target.setName( toUpperCase( source.getName() ) );

        return target;
    }
}

nullValueMappingStrategy

如果 source 为 null 时,对应的 target 的处理策略,默认是 NullValueMappingStrategy.RETURN_NULL ,即 target 中对应的字段也设置为 null。

但有时候设置为 null 可能不符合我们的需求,比如 target 中有个 List ids,我们希望如果 source 中ids 为 null 时,target 的 ids 设置为空 list。这时候可以使用 nullValueMappingStrategy 策略中的 NullValueMappingStrategy.RETURN_DEFAULT

nullValueMappingStrategy 可以使用在某个方法上(只对该方法生效),也可以使用在类上(对类中的所有方法都生效),如下:

@Component
public class MyMapperImpl implements MyMapper {

    @Override
    public Target toTarget(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        target.setName( source.getName() );
        List list = source.getIds();
        if ( list != null ) {
            target.setIds( new ArrayList( list ) );
        }
        else {
            target.setIds( null );
        }

        return target;
    }
}

指定 NullValueMappingStrategy.RETURN_DEFAULT 策略后:

@Mapper(componentModel = "spring",
        nullValueMappingStrategy = org.mapstruct.NullValueMappingStrategy.RETURN_DEFAULT)
public interface MyMapper {

    Target toTarget(Source source);
}

@Component
public class MyMapperImpl implements MyMapper {
    @Override
    public Target toTarget(Source source) {

        Target target = new Target();

        if ( source != null ) {
            target.setName( toUpperCase( source.getName() ) );
            List list = source.getIds();
            if ( list != null ) {
                target.setIds( new ArrayList( list ) );
            }
            else {
                target.setIds( new ArrayList() );
            }
        }

        return target;
    }
}

可以看到,当 source 或者 source.ids 为 null 时,返回的 target 和 target.ids 都是默认的空值(空对象+空 list)。

Decorator

你可以通过创建一个 Decorator 类来对你的方法进行修饰并实现全局处理。

以下是一个例子:

public abstract class YourMapperDecorator implements YourMapper {

    private final YourMapper delegate;

    public YourMapperDecorator(YourMapper delegate) {
        this.delegate = delegate;
    }

    @Override
    public Target toTarget(Source source) {
        Target result = delegate.toTarget(source);
        if (result != null) {
            if (result.getField() == null) {
                result.setField("");
            }
            // 你可以在这里对其他字段进行同样的处理...
        }
        return result;
    }
}

然后你只需在你的 Mapper 接口上添加 @DecoratedWith 注解:

@Mapper
@DecoratedWith(YourMapperDecorator.class)
public interface YourMapper 
{
    Target toTarget(Source source);
}

这样,每次调用 toTarget 方法时, YourMapperDecorator 中的实现会被调用。在这里,你可以实现任何你想要的逻辑,例如对空字段赋予特定的默认值。







请到「今天看啥」查看全文