MapStruct 進(jìn)階技巧:提升代碼效率
前言
MapStruct 是一個(gè) Java 編譯時(shí)注解處理框架,用來自動(dòng)化將一種 Java Bean 對(duì)象映射成另一種類型的對(duì)象。
該框架的主要目標(biāo)是使開發(fā)人員在盡可能少的代碼和最低的運(yùn)行時(shí)間成本下實(shí)現(xiàn)屬性映射。MapStruct 通過在編譯時(shí)生成代碼來實(shí)現(xiàn)這點(diǎn),這與大多數(shù)其他 Java Bean 映射框架在運(yùn)行時(shí)通過反射進(jìn)行映射形成了鮮明對(duì)比。
MapStruct 具有以下主要特性:
- 簡(jiǎn)潔: 簡(jiǎn)化了 Java Beans 之間轉(zhuǎn)換的代碼,自動(dòng)生成使用簡(jiǎn)單的賦值語句完成的映射實(shí)現(xiàn)。
 - 性能優(yōu)秀: 由于 MapStruct 是在編譯時(shí)生成代碼,不涉及任何反射,因此執(zhí)行映射的性能優(yōu)越。
 - 安全: 通過在編譯時(shí)生成映射代碼,MapStruct 提供了類型安全的映射,并能在編譯時(shí)就發(fā)現(xiàn)潛在的錯(cuò)誤。
 - 靈活: 可通過自定義轉(zhuǎn)換方法、類型轉(zhuǎn)換和映射策略等來滿足復(fù)雜的映射需求。
 - 良好的 IDE 支持: 由于 MapStruct 是編譯時(shí)工具,所以擁有良好的 IDE 集成,如代碼自動(dòng)完成、錯(cuò)誤高亮等。
 
總的來說, MapStruct 是一個(gè)強(qiáng)大且靈活的映射框架,很好的解決有關(guān)對(duì)象轉(zhuǎn)換的問題,實(shí)現(xiàn)了代碼的簡(jiǎn)潔和性能的兼顧。MapStruct的常規(guī)用法,網(wǎng)上有很多教程了,本文將列舉一些進(jìn)階用法,方便日常開發(fā)使用。
expression
在轉(zhuǎn)化的時(shí)候,執(zhí)行 java 表達(dá)式,直接看例子:
@Mapper(componentModel = 'spring')
public interface MyMapper {
    @Mapping(target = 'createTime', expression = 'java(System.currentTimeMillis())')
    Target toTarget(Source source);
}轉(zhuǎn)化成 target 對(duì)象時(shí),createTime字段的值,會(huì)設(shè)置為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
做映射時(shí),默認(rèn)情況下,從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);
}生成的轉(zhuǎn)化代碼類如下:
@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 轉(zhuǎn)化成大寫格式,那么可以使用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) {
        // 這里寫轉(zhuǎn)換大寫的邏輯
        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 時(shí),對(duì)應(yīng)的 target 的處理策略,默認(rèn)是 NullValueMappingStrategy.RETURN_NULL,即 target 中對(duì)應(yīng)的字段也設(shè)置為 null。
但有時(shí)候設(shè)置為 null 可能不符合我們的需求,比如 target 中有個(gè) List ids,我們希望如果 source 中ids 為 null 時(shí),target 的 ids 設(shè)置為空 list。這時(shí)候可以使用nullValueMappingStrategy策略中的NullValueMappingStrategy.RETURN_DEFAULT。
nullValueMappingStrategy 可以使用在某個(gè)方法上(只對(duì)該方法生效),也可以使用在類上(對(duì)類中的所有方法都生效),如下:
@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<Integer> list = source.getIds();
        if ( list != null ) {
            target.setIds( new ArrayList<Integer>( 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<Integer> list = source.getIds();
            if ( list != null ) {
                target.setIds( new ArrayList<Integer>( list ) );
            }
            else {
                target.setIds( new ArrayList<Integer>() );
            }
        }
        return target;
    }
}可以看到,當(dāng) source 或者 source.ids 為 null 時(shí),返回的 target 和 target.ids 都是默認(rèn)的空值(空對(duì)象+空 list)。
Decorator
你可以通過創(chuàng)建一個(gè) Decorator 類來對(duì)你的方法進(jìn)行修飾并實(shí)現(xiàn)全局處理。
以下是一個(gè)例子:
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('');
            }
            // 你可以在這里對(duì)其他字段進(jìn)行同樣的處理...
        }
        return result;
    }
}然后你只需在你的 Mapper 接口上添加 @DecoratedWith 注解:
@Mapper
@DecoratedWith(YourMapperDecorator.class)
public interface YourMapper {
    Target toTarget(Source source);
}這樣,每次調(diào)用 toTarget 方法時(shí),YourMapperDecorator 中的實(shí)現(xiàn)會(huì)被調(diào)用。在這里,你可以實(shí)現(xiàn)任何你想要的邏輯,例如對(duì)空字段賦予特定的默認(rèn)值。















 
 
 
















 
 
 
 