免费观看又色又爽又黄的小说免费_美女福利视频国产片_亚洲欧美精品_美国一级大黄大色毛片

秒懂Java之實體轉化利器MapStruct詳解-創新互聯

[版權申明]非商業目的注明出處可自由轉載
出自:shusheng007

十多年專注成都網站制作,企業網站制作,個人網站制作服務,為大家分享網站制作知識、方案,網站設計流程、步驟,成功服務上千家企業。為您提供網站建設,網站制作,網頁設計及定制高端網站建設服務,專注于企業網站制作,高端網頁制作,對小攪拌車等多個行業,擁有豐富的網站設計經驗。

文章目錄
  • 概述
  • MapStruct
    • 簡介:
    • 使用
      • 如何配置
    • 應用
    • 進階
      • 裝插件
      • 調用方式
      • 自定義映射
      • 忽略映射
      • 設置默認值
      • 設置常量
      • 數據類型轉換
      • 表達式
      • 嵌套映射
      • 集合映射
      • 外部引用
      • 多個數據源
      • 切面操作
  • 總結
  • 源碼

概述

由于現代程序在追求擴展和維護性時很多采用分層的設計結構,所以在寫程序時候需要在各種實體之間互相轉換,而他們之間很多時候在業務或者技術架構上區別較大,在具體的屬性上差別卻很小。

例如將Programer轉換為ProgramerDto就很普遍,如下所示:

public class Programer {
    private String name;
    private String proLang;
}

轉換為:

public class ProgramerDto {
    private String name;
    private String proLang;
}

由于這些是繁瑣易錯且沒有技術含量的編碼工作,所以聰明的程序員就會尋求不斷簡化它的方法,MapStruct就是其中的一個利器。

MapStruct 簡介:

MapStruct is a Java annotation processor for the generation of type-safe and performant mappers for Java bean classes

大意就是:MapStruct 是一個用于Java的Bean的映射器,是它是基于注解的,而且是編譯時APT(annotation processor tool)。不像其他APT是運行時,例如Spring里面的注解處理方式,是在運行時通過反射的方式處理的。

詳細介紹可以到其官網查看:MapStruct源碼,下面是官方給出的選擇MapStruc的理由,你看看是否說服了你去使用它:

  • Fast execution by using plain method invocations instead of reflection
  • Compile-time type safety. Only objects and attributes mapping to each other can be mapped, so there’s no accidental mapping of an order entity into a customer DTO, etc.
  • Self-contained code—no runtime dependencies
  • Clear error reports at build time if:
    • mappings are incomplete (not all target properties are mapped)
    • mappings are incorrect (cannot find a proper mapping method or type conversion)
  • Easily debuggable mapping code (or editable by hand—e.g. in case of a bug in the generator)
使用 如何配置

從前面的介紹我們得知,MapStruct是通過在編譯時通過注解來生成代碼的方式工作的,所以需要配置APT。此處我們還想使用lombok,所以也會順便配置其與lombok結合的配置。

UTF-81.81.81.5.3.Final1.18.200.2.0org.mapstructmapstruct${mapstruct.version}org.projectlomboklombok${lombok.version}provided		...
      org.apache.maven.pluginsmaven-compiler-plugin 3.8.1  ${maven.compiler.source} ${maven.compiler.target}
                  org.mapstructmapstruct-processor ${mapstruct.version}   org.projectlomboklombok ${lombok.version}   org.projectlomboklombok-mapstruct-binding ${lombok-mapstruct-binding.version}    

如上所示,主要配置了注解處理器:。如果不使用lombok的話,去掉相應的配置即可。

應用

當完成了配置就就需要寫代碼了,主要是一些注解的使用,MapStruc提供的功能是很強大的,但是入門很容易的。

假設我們有如下兩個需要轉換的類:

@Data
public class Programer {
    private String name;
    private String lang;
    private Double height;
    private Date beDate;
    private Address address;
    private String girlName;
    private String girlDes;
}

@Data
public class ProgramerDto {
    private String name;
    private String proLang;
    private String height;
    private String beDate;
    private AddressDto address;
    private GirlFriendDto girlFriend;
}

第一步: 定義一個interface,使用@Mapper標記

@Mapper
public interface ProgramerConvetor {
...
}

第二步:構建一個實例屬性用于訪問里面的方法。

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

第三步:提供轉換方法申明,必要時使用@Mapping注解

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

    @Mapping(target = "lang", source = "proLang")
    ProgramerDto toProgramerDto(Programer programer);
}

MapStruc默認會將兩個bean的名稱相同的屬性進行映射,如果source與target的屬性名稱不一致則需要借助@Mapping注解。

簡單的轉換就只需要以上3步就可以了,編譯程序后就會在\target\generated-sources\annotations下產生實現類了。

下面的代碼是MapStruc自動生成的:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-01-08T16:51:05+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 11.0.16.1 (Oracle Corporation)"
)
public class ProgramerConvetorImpl implements ProgramerConvetor {

    @Override
    public ProgramerDto toProgramerDto(Programer programer) {
        if ( programer == null ) {
            return null;
        }

        ProgramerDto programerDto = new ProgramerDto();
        programerDto.setLang( programer.getProLang() );
        programerDto.setName( programer.getName() );
        ...
        return programerDto;
    }
}

是不是和你手寫的也差不多,那有了生成類我們就可以在代碼中使用了:

public void runMap(){
    Programer programer = new Programer();
    programer.setName("shusheng007");
    ...
    
    ProgramerDto programerDto = ProgramerConvetor.INSTANCE.toProgramerDto(programer);
    log.info("dto: {}",programerDto);
}

可見,可以通過轉換器接口里面的那個INSTANCE實例屬性來調用其方法??词遣皇潜饶闶謱懛奖愣嗔四??特別是屬性比較多,而其名稱又有很多一致的情況下就更方便了。

進階

前面那個是最基礎的使用,MapStruc提供了非常靈活的映射方式,要完全掌握既沒有必要又是不可能的,下面我們挑幾個常用的以應對80%的日常工作。

裝插件

工欲善其事必先利其器,咱給Idea裝上一個插件 MapStruct Support,各種代碼智能提示走起來…

調用方式

前面我們使用在接口中定義一個實例屬性的方式來訪問生成的方法,這有點不Spring,在Spring中我們習慣將bean交給Spring容器管理,MapSturc也支持。

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface ProgramerConvetor {
}

通過修改@MappercomponentModel的屬性為spring即可。下面是生成的代碼,發現已經添加了@Component注解。

...
@Component
public class ProgramerConvetorImpl implements ProgramerConveto{
}
自定義映射

當source與target里的屬性名稱不一致時需要顯示指定映射關系

@Mapping(target = "lang", source = "proLang")
ProgramerDto toProgramerDto(Programer programer);

生成代碼:

ProgramerDto programerDto = new ProgramerDto();
programerDto.setLang( programer.getProLang() );
忽略映射

如果不想給ProgramerDtoproLang賦值可以忽略它。

@Mapping(target = "proLang", ignore = true)
ProgramerDto toProgramerDto(Programer programer);
設置默認值

如果想實現在source值為null時給一個默認值也是可以了。

@Mapping(target = "proLang", defaultValue = "java")
ProgramerDto toProgramerDto(Programer programer);

生成代碼:

ProgramerDto programerDto = new ProgramerDto();
if ( programer.getProLang() != null ) {
    programerDto.setProLang( programer.getProLang() );
}
else {
    programerDto.setProLang( "java" );
}

其實默認值不僅可以是一個具體的值,還可以是一個表達式,表達式一會我們再說。

設置常量

給source的某個屬性賦值為常量

@Mapping(target = "proLang", constant = "kotlin")
ProgramerDto toProgramerDto(Programer programer);

生成代碼:

programerDto.setProLang( "kotlin" );
數據類型轉換

我們在進行bean映射的時候,有時會遇到數據類型不一致的情況。例如對于一個日期,source的數據類型是Date,而target的數據類型是String,這些情況怎么處理呢?

public class Programer {
    private Double height;
    private Date beDate;
}
public class ProgramerDto {
    private String height;
    private String beDate;
}

從上面的代碼可以看到,我們的兩個bean的數據類型是不一致的,但是MapStruct卻可以幫我們自動轉換

@Mapping(target = "height", source = "height")
ProgramerDto toProgramerDto(Programer programer);

生成的代碼:

if ( programer.getHeight() != null ) {
    programerDto.setHeight( String.valueOf( programer.getHeight() ) );
}

生成的代碼已經將Double幫我們轉換成String了。不僅如此,我們還可以對生成的字符串的格式進行設置,例如將身高數據保留兩位小數

@Mapping(target = "height", source = "height" ,numberFormat = "#.00")

生成的代碼:

if ( programer.getHeight() != null ) {
    programerDto.setHeight( new DecimalFormat( "#.00" ).format( programer.getHeight() ) );
}

下面看一個日期相關的轉換

@Mapping(target = "beDate", dateFormat = "yyyy-MM-dd HH:mm:ss")
ProgramerDto toProgramerDto(Programer programer);

生成的代碼:

if ( programer.getBeDate() != null ) {
    programerDto.setBeDate( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format( programer.getBeDate() ) );
}

可見在日期轉換中我們可以控制器轉換后的格式。

表達式

這個就比較厲害了,你就認為是方法調用即可,我喜歡。有時我們會遇到在做映射的時候不是簡單的賦值,而是要進行計算,那這個功能就可以使用表達式來完成。

例如我們要實現將程序員名稱變成大寫個功能,就可以使用expression這個屬性進行配置。表達式的形式如下:java(代碼調用)

@Mapping(target = "name", expression = "java(programer.getName().toUpperCase())")
ProgramerDto toProgramerDto(Programer programer);

生成代碼:

programerDto.setName( programer.getName().toUpperCase() );

表達式里可以進行方法調用,例如上面的代碼我們可以換一種方式寫,將轉換代碼寫成一個default函數。

注意這個defalut方法的簽名一定要符合你的需求,因為MS會為每一個映射嘗試這個方法,一旦符合了就會被使用,例如你寫一個String 到 String的轉換那就麻煩了,每個符合這個得屬性轉換都會用上…

@Mapping(target = "name", expression = "java(nameToUp(programer))")
ProgramerDto toProgramerDto(Programer programer);

default String nameToUp(Programer programer){
    return Optional.ofNullable(programer)
            .filter(Objects::nonNull)
            .map(p->p.getName())
            .orElse(null)
            .toUpperCase();
}

對于分不清的情況所可以使用和Spring類似的方案,就是使用qualified。例如上面的功能還可以以下面的方案實現。

@Mapping(target = "name", qualifiedByName ={"nameToUp"} )
ProgramerDto toProgramerDto(Programer programer);

@Named("nameToUp")
default String nameToUp(String name) {
    return name.toUpperCase();
}
嵌套映射

我們經常會遇到bean里面套著bean的映射。

{
    "name":"shusheng007",
    "address":{
        "country":"China",
        "city":"TianJin"
    }
}

對于這樣的映射,我們只需要在mapper中提供一個嵌套bean的轉換關系即可。

@Mapping(target = "address", source = "address")
ProgramerDto toProgramerDto(Programer programer);
//嵌套bean的轉換關系
AddressDto toAddressDto(Address addr);

生成代碼:

programerDto.setAddress( toAddressDto( programer.getAddress() ) );

@Override
public AddressDto toAddressDto(Address addr) {
    if ( addr == null ) {
        return null;
    }

    AddressDto addressDto = new AddressDto();

    addressDto.setCountry( addr.getCountry() );
    addressDto.setCity( addr.getCity() );

    return addressDto;
}

其實MapStruct非常智能的,即使你不提供它也會嘗試進行映射的。

集合映射

只需要提供集合元素類型的映射即可。

AddressDto toAddressDto(Address addr);

List toAddressList(List addrList);

生成代碼:

@Override
public AddressDto toAddressDto(Address addr) {
    ...
}

@Override
public List toAddressList(List addrList) {
    if ( addrList == null ) {
        return null;
    }

    List list = new ArrayList( addrList.size() );
    for ( Address address : addrList ) {
        list.add( toAddressDto( address ) );
    }

    return list;
}
外部引用

上面我們介紹了表達式,通過它我們可以寫代碼邏輯,但是當轉換關系需要調用外部類的方法時怎么辦呢?我們有兩種方法,下面看一下。

例如我們有如下要被引用的類:

@Component
public class GirlFriendMapper {

    public GirlFriendDto toGirlFriendDto(Programer programer) {
        GirlFriendDto girlFriendDto = new GirlFriendDto();
        girlFriendDto.setName(programer.getName());
        girlFriendDto.setDescription(programer.getGirlDes());
        return girlFriendDto;
    }
}
  • 使用抽象類代替接口來做Mapper
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public abstract class ClzProgramerConvertor {
    @Autowired
    protected GirlFriendMapper girlFriendMapper;
    ...
    @Mapping(target = "girlFriend", expression = "java(girlFriendMapper.toGirlFriendDto(programer))")
    public abstract ProgramerDto toProgramerDto(Programer programer);
}

使用了抽象類后,你發現熟悉的味道回來了,可以使用@Autowired隨便往里面注入實例了,然后在expression里面調用就好了,是不是很爽?

生成代碼:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-01-08T21:03:05+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 11.0.16.1 (Oracle Corporation)"
)
@Component
public class ClzProgramerConvertorImpl extends ClzProgramerConvertor {

    @Override
    public ProgramerDto toProgramerDto(Programer programer) {
        ProgramerDto programerDto = new ProgramerDto();
		...
        programerDto.setGirlFriend( girlFriendMapper.toGirlFriendDto(programer) );

        return programerDto;
    }

    protected AddressDto addressToAddressDto(Address address) {
     ...
    }
}

那個girlFriendMapper就是我們在父類中注入的。

  • 使用@Mapper注解的import和use屬性

import屬性就和java中的import是一樣的,導入后在expression中就可以不使用類的全限定名稱了。例如你的轉換用到了一個靜態工具類,那么如果不在import中導入此工具類,那么使用的時候就要全限定名了。

@Mapping(target = "name", expression = "java(top.ss007.Util.toUpper(programer.getName()))")

當使用的映射方法在其他非靜態類里時,就可以使用use屬性。

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING,
        uses = {
            GirlFriendMapper.class
        }
)
public interface ProgramerConvetor {
    @Mapping(target = "girlFriend", source = "programer")
    ProgramerDto toProgramerDto(Programer programer);
}

我們使用@Mapperuse屬性將GirlFriendMapper引入進來。

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-01-08T21:03:17+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 11.0.16.1 (Oracle Corporation)"
)
@Component
public class ProgramerConvetorImpl implements ProgramerConvetor {
     @Autowired
    private GirlFriendMapper girlFriendMapper;

    @Override
    public ProgramerDto toProgramerDto(Programer programer) {
        ProgramerDto programerDto = new ProgramerDto();
        ...
        programerDto.setGirlFriend( girlFriendMapper.toGirlFriendDto( programer ) );
        return programerDto;
    }
 }

可見,引入的類的實例在實現類中被注入了。我們還可以通過injectionStrategy = InjectionStrategy.CONSTRUCTOR指定通過構造函數來注入實例,如果不指定默認使用屬性注入。

多個數據源

有時我們會遇到多個bean轉一個bean的情況,需顯示指定參數名稱

@Mapping(target = "name", source = "programer.name")
@Mapping(target = "girlFriendName", source = "girl.name")
ProgramerDto toProgramerDto(Programer programer, Gir girl);
切面操作

MapStruct 還提供了兩個注解@BeforeMapping, @AfterMapping用來實現在mapping前后的統一操作,這一般比較少用,但是在使用多態的時候還是很有作用的。

需求:我們有一個Human父類,有男人和女人兩個子類,然后我們要將這兩個子類型mapping成HumanDto。HumanDto中有個性別的屬性,需要根據具體的類型決定。在mapping完成后,我們還要將名稱修飾一下。

public class Human {
    private String name;
}
public class Man extends Human{
}
public class Woman extends Human{
}

public class HumanDto {
    private String name;
    private GenderType genderType;
}
public enum GenderType {
    MAN,WOMAN
}

當然我們可以寫兩個轉換方法即可,一個Man到HumanDto,一個Woman到HumanDto,但是在使用的時候就比較麻煩了,需要傳入具體的類型,代碼也有重復。這種場景下我們就可以使用這兩個注解完美的解決問題。

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public abstract class HumanConvertor {

    @BeforeMapping
    protected void humanDtoWithGender(Human human, @MappingTarget HumanDto humanDto) {
        if (human instanceof Man) {
            humanDto.setGenderType(GenderType.MAN);
        } else if (human instanceof Woman) {
            humanDto.setGenderType(GenderType.WOMAN);
        }
    }

    @AfterMapping
    protected void decorateName(@MappingTarget HumanDto humanDto) {
        humanDto.setName(String.format("【%s】", humanDto.getName()));
    }

    public abstract HumanDto toHumanDto(Human human);
}

生成的代碼:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-01-09T23:45:44+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 11.0.16.1 (Oracle Corporation)"
)
@Component
public class HumanConvertorImpl extends HumanConvertor {

    @Override
    public HumanDto toHumanDto(Human human) {
        ...
        HumanDto humanDto = new HumanDto();
        //mapping前執行
        humanDtoWithGender( human, humanDto );
        humanDto.setName( human.getName() );
        //mapping后執行
        decorateName( humanDto );

        return humanDto;
    }
}

使用:

@Autowired
private HumanConvertor humanConvertor;

public void runHumanDemo(){
    Human man = new Man();
    man.setName("王二狗");
    log.info("{}是大男人",humanConvertor.toHumanDto(man));

    Human woman = new Woman();
    woman.setName("牛翠華");
    log.info("{}是小女人", humanConvertor.toHumanDto(woman));
}
總結

至此,MapStruct的基本操作基本上都涉及到了,足以應對日常工作了,但是我還是那句話:那年我雙手插兜,不知道什么叫… , 哎呀我去,跑偏了。 我還是那句話:MapStruct提供了大量的注解和自定義配置,如遇到特殊需求還需要去查看官方文檔和示例。

忽忽悠悠又要過年啦,今年終于可以回老家過年了, 3年疫情終于要結束了,如果惡毒生活‘’強奸”了你,在反抗不了的情況下還是要學會隱忍和享受,保持樂觀的心態,不斷學習以待反抗之時,去追求美好生活…

源碼

源碼請到首發文末查看:MapSturct

你是否還在尋找穩定的海外服務器提供商?創新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧

分享標題:秒懂Java之實體轉化利器MapStruct詳解-創新互聯
文章來源:http://m.newbst.com/article14/dgjsge.html

成都網站建設公司_創新互聯,為您提供標簽優化、品牌網站建設全網營銷推廣、移動網站建設、軟件開發動態網站

廣告

聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯

外貿網站制作