BeanUtilsツール類を捨てましょう.MapStructはいいですね.


BeanUtilsツールクラスMapStructを使用する前に、オブジェクトを変換しました.
package com.spring.security.demo.utils.bean;

import org.springframework.beans.BeanUtils;

/**
 * @author wulongbo
 * @version 1.0
 * @date 2020/6/11 9:17
 * @description
 */
public class BabaBeanUtils {

    /**
     * dot    Do    
     *
     * @param voEntity
     * @param dtoClass
     * @return
     */
    public static  Dto voToDto(Object voEntity, Class dtoClass) {
        //   VoSF    !
        if (voEntity == null) {
            return null;
        }
        //   DtoClass     
        if (dtoClass == null) {
            return null;
        }
        try {
            Dto newInstance = dtoClass.newInstance();
            BeanUtils.copyProperties(voEntity, newInstance);
            // Dto  Do
            return newInstance;
        } catch (Exception e) {
            return null;
        }
    }

    //          
}

後の読者はBeanUtilsを参照してPersonConverterインタフェースを汎用的に変更することができる.
なぜMapStructが必要なのですか?
まず、MapStructのようなフレームワークがどのようなシーンに適しているのか、なぜ市販されているのかについてお話しします.
ソフトウェアアーキテクチャ設計では、階層構造が最も一般的であり、最も重要な構造でもあります.多くの人は3層アーキテクチャ、4層アーキテクチャなどに慣れていない.
「コンピューター科学分野のいかなる問題も間接的な中間層を増やすことで解決でき、だめなら2層を加える」という人もいる.
しかし、ソフトウェアアーキテクチャの階層化が進むにつれて、各階層間のデータモデルは相互変換の問題に直面し、典型的には、DO、DTO、VOなどのコードに様々なOを見ることができる.
一般的に、同じデータモデルでは、異なる階層で異なるデータモデルを使用します.データストレージ層では、DOを使用してビジネスエンティティを抽象化します.ビジネスロジック層では、DTOを使用してデータ転送オブジェクトを表します.展示層に着くと,オブジェクトをVOにカプセル化してフロントエンドと対話する.
では、データの先端からデータ永続化層(先端まで透過)にデータの先端から透過するには、オブジェクト間の相互変換、すなわち異なるオブジェクトモデル間でマッピングを行う必要がある.
通常、get/setなどの方法でフィールドマッピング操作を1つずつ行うことができます.
personDTO.setName(personDO.getName());

personDTO.setAge(personDO.getAge());

personDTO.setSex(personDO.getSex());

personDTO.setBirthday(personDO.getBirthday());

しかし,このようなマッピングコードの作成は冗長でエラーが発生しやすいタスクである.MapStructなどの類似のフレームワークの目標は,自動化によりこの作業をできるだけ多く簡略化することである.
MapStructの使用
MapStruct(https://mapstruct.org/)は、「コンフィギュレーションよりも規則が優れている」方法に基づくJava beanタイプ間のマッピングの実装を大幅に簡略化したコードジェネレータである.生成されたマッピングコードは、純粋な方法で呼び出されるため、迅速で、タイプが安全で、理解しやすい.
      ,        ,         ,                 ,       ,       。

PersonDOとPersonDTOの2つのクラスが相互変換される必要があると仮定します.クラス定義は次のとおりです.
package com.spring.security.demo.Do;

import lombok.Data;

import java.util.Date;

/**
 * @Author wulongbo
 * @Date 2021/1/6 16:36
 * @Version 1.0
 */
@Data
public class PersonDO {

    private Integer id;

    private String name;

    private int age;

    private Date birthday;

    private String gender;

    private String address;

}
package com.spring.security.demo.dto;

import com.spring.security.demo.base.GenderEnum;
import com.spring.security.demo.entity.HomeAddress;
import lombok.Data;

import java.util.Date;

@Data
public class PersonDTO {

    public String userName;

    private Integer age;

    private Date birthday;

    private GenderEnum gender;

    private HomeAddress address;

}
package com.spring.security.demo.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author wulongbo
 * @Date 2021/1/6 17:26
 * @Version 1.0
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
public class HomeAddress {

    private String province;

    private String city;

    private String area;

    private String street;

    private String towns;

    private String community;

    //    
    private String houseNums;
}

MapStructを用いたbeanマッピングの方法を実証した.MapStructを使用するには、まず関連するjarパッケージに依存する必要があります.maven依存方式を使用すると、次のようになります.


    
        org.mapstruct
        mapstruct-jdk8
        1.0.0.Final
    

    
        org.mapstruct
        mapstruct-processor
        1.0.0.Final
    

その後、マッピングを行うインタフェースを定義する必要があります.主なコードは次のとおりです.
package com.spring.security.demo.service;

import com.alibaba.fastjson.JSON;
import com.spring.security.demo.Do.PersonDO;
import com.spring.security.demo.dto.PersonDTO;
import com.spring.security.demo.entity.HomeAddress;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

import java.util.Map;

/**
 * @Author wulongbo
 * @Date 2021/1/6 16:38
 * @Version 1.0
 */

@Mapper
public interface PersonConverter {

    PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);

    @Mappings({
            @Mapping(source = "name", target = "userName"),
            @Mapping(target = "address", ignore = true)
    })
    @Mapping(target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
    PersonDTO do2dto(PersonDO person);

    @Mappings({@Mapping(expression = "java(map.get(\"province\"))", target = "province"),
            @Mapping(expression = "java(map.get(\"city\"))", target = "city"),
            @Mapping(expression = "java(map.get(\"area\"))", target = "area"),
            @Mapping(expression = "java(map.get(\"street\"))", target = "street"),
            @Mapping(expression = "java(map.get(\"towns\"))", target = "towns"),
            @Mapping(expression = "java(map.get(\"community\"))", target = "community"),
            @Mapping(expression = "java(map.get(\"houseNums\"))", target = "houseNums")})
    HomeAddress map2HomeAddress(Map map);


    @Mappings(@Mapping(source = "userName", target = "name"))
    @Mapping(target = "address", expression = "java(homeAddressToString(dto2do.getAddress()))")
    PersonDO dto2do(PersonDTO dto2do);

    default String homeAddressToString(HomeAddress address) {
        return JSON.toJSONString(address);
    }

}

注記@Mapperを使用してConverterインタフェースを定義し、do 2 dtoメソッドを定義します.メソッドのパラメータタイプはPersonDOで、パラメータタイプはPersonDTOです.このメソッドはPersonDOをPersonDTOに変換するために使用されます.テストコードは次のとおりです.
package com.spring.security.demo;

import com.spring.security.demo.Do.PersonDO;
import com.spring.security.demo.base.GenderEnum;
import com.spring.security.demo.dto.PersonDTO;
import com.spring.security.demo.entity.HomeAddress;
import com.spring.security.demo.service.PersonConverter;
import org.springframework.security.core.parameters.P;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author wulongbo
 * @Date 2021/1/6 16:48
 * @Version 1.0
 */
public class do2Dto {
    public static void main(String[] args) {

        Map map = new HashMap();
        map.put("province", "  ");
        map.put("city", "  ");
        map.put("area", "  ");
        map.put("street", "  ");
        map.put("towns", "  ");
        map.put("community", "  ");
        map.put("houseNums", "1001");
        HomeAddress homeAddress = PersonConverter.INSTANCE.map2HomeAddress(map);
        System.out.println(homeAddress.toString());

        PersonDO personDO = new PersonDO();

        personDO.setName("wlb");

        personDO.setAge(26);

        personDO.setBirthday(new Date());

        personDO.setId(1);

        personDO.setGender(GenderEnum.MALE.name());

        PersonDTO personDTO = PersonConverter.INSTANCE.do2dto(personDO);

        personDTO.setAddress(homeAddress);
        System.out.println(personDTO);

        PersonDO personDO1 = PersonConverter.INSTANCE.dto2do(personDTO);
        System.out.println(personDO1);
    }
}

出力結果:
HomeAddress(province=  , city=  , area=  , street=  , towns=  , community=  , houseNums=1001)
PersonDTO(userName=wlb, age=26, birthday=Thu Jan 07 11:33:26 CST 2021, gender=MALE, address=HomeAddress(province=  , city=  , area=  , street=  , towns=  , community=  , houseNums=1001))
PersonDO(id=null, name=wlb, age=26, birthday=Thu Jan 07 11:33:26 CST 2021, gender=MALE, address={"area":"  ","city":"  ","community":"  ","houseNums":"1001","province":"  ","street":"  ","towns":"  "})

私たちはMapStructを使ってPersonDOをPersonDTOに完璧に変えたことがわかります.上のコードから分かるように,MapStructの使い方は比較的簡単で,@Mapper注釈に主に依存している.
しかし、多くの場合、互いに変換する必要がある2つのクラス間の属性名、タイプなどが完全に一致していない場合や、直接マッピングしたくない場合もあることを知っています.では、どのように処理すればいいのでしょうか.実はMapStructもこの方面でよくやっています.
MapStruct処理フィールドマッピング
まず、変換する2つのクラスのソースオブジェクト属性がターゲットオブジェクト属性のタイプと名前と一致すると、対応する属性が自動的にマッピングされることを明確に示すことができます.
では、もし特別な状況に遭遇したらどう処理しますか?
名前が一致しないマッピング方法
上記の例では、PersonDOではユーザ名をnameで表し、PersonDTOではuserNameでユーザ名を表していますが、パラメータマッピングはどのように行われますか.@Mapping注記を使用するには、メソッド署名に注記を使用し、変換するソースオブジェクトの名前とターゲットオブジェクトの名前を指定するだけです.名前の値をuserNameにマッピングするには、次のようにします.
@Mapping(source = "name", target = "userName")

自動マッピング可能なタイプ
名前の不一致に加えて、上記の例のようにPersonDOではStringタイプでユーザの性別を表し、PersonDTOではGenterの列挙でユーザの性別を表す特殊な場合がある.
このときタイプが一致しない場合は,相互変換の問題にかかわる必要がある.
実際には、MapStructは一部のタイプを自動的にマッピングし、例のようにStringタイプを自動的に列挙タイプに変換する必要はありません.
一般的には、次の場合に自動タイプ変換を行います.
  • 基本タイプおよびそれらに対応する包装タイプ.
  • 基本タイプのパッケージタイプとStringタイプの間の
  • Stringタイプと列挙タイプの間の
  • カスタム定数
    マッピングの変換中に一定の値を定義する場合はconstantを使用します.
    @Mapping(source = "name", constant = "wlb")

    タイプが一致しないマッピング方法
    また、上記の例では、Personというオブジェクトに住所という属性を追加する必要がある場合、PersonoDTOではHomeAddressクラスを個別に定義して住所を表すのが一般的ですが、PersonクラスではStringタイプを使用して住所を表すのが一般的です.
    これはHomeAddressとStringの間でJSONを用いて相互変換する必要があり,この場合もMapStructはサポートできる.
    PersonConverterでメソッドを定義するだけです(PersonConverterはインタフェースなのでJDK 1.8以降のバージョンでdefaultメソッドを定義できます).このメソッドの役割はHomeAddressをStringタイプに変換することです.
    default  :Java 8          ,    default   , default      ,      ,                 

    次に、dto 2 doメソッドでは、次の注釈によってタイプの変換を実現することができる.
    @Mapping(target = "address",expression = "java(homeAddressToString(dto2do.getAddress()))")

    これはカスタムタイプ変換であり、StringとDateの間の変換など、MapStruct自体がサポートしているタイプ変換もあります.
    @Mapping(target = "birthday",dateFormat = "yyyy-MM-dd HH:mm:ss")

    で、一般的なフィールドマッピングの方法を簡単に紹介し、私自身が仕事でよく遭遇するいくつかのシーンでもあります.詳細については、公式の例を参照してください(https://github.com/mapstruct/...).
    MapStructのパフォーマンス
    前述したように多くのMapStructの使い方は、MapStructの使い方が簡単で、フィールドマッピングの機能が強いことがわかりますが、彼の性能はどうでしょうか.
    なぜアリババはApache Beanutilsを使用して属性のcopyを禁止したのですか?」の例を参照して、MapStructのパフォーマンステストを行います.
    1000、10000、100000、100000のマッピングをそれぞれ実行する時間は、0 ms、1 ms、3 ms、6 msである.
    MapStructの消費時間は他のいくつかのツールに比べて非常に短いことがわかります.
    では、なぜMapStructの性能がこんなに良いのでしょうか.
    実際、MapStructと他のいくつかのフレームワークの最大の違いは、他のマッピングフレームワークと比較して、MapStructがコンパイル時にbeanマッピングを生成することであり、高性能を確保し、問題を事前にフィードバックすることができ、開発者が徹底的にエラーチェックを行うことができることです.
    MapStructの依存を導入した場合、特にmaven-compiler-pluginにmapstruct-processorのサポートが追加されましたか?
    さらに,我々はコードに多くのMapStructが提供する注釈を用い,コンパイル期間中にMapStructがbeanマッピングのコードを直接生成することができ,我々の代わりに多くのsetterとgetterを書いたことに相当する.コードに次のMapperを定義します.
    package com.spring.security.demo.service;
    
    import com.alibaba.fastjson.JSON;
    import com.spring.security.demo.Do.PersonDO;
    import com.spring.security.demo.dto.PersonDTO;
    import com.spring.security.demo.entity.HomeAddress;
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.Mappings;
    import org.mapstruct.factory.Mappers;
    
    import java.util.Map;
    
    /**
     * @Author wulongbo
     * @Date 2021/1/6 16:38
     * @Version 1.0
     */
    
    @Mapper
    public interface PersonConverter {
    
        PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);
    
        @Mappings({
                @Mapping(source = "name", target = "userName"),
                @Mapping(target = "address", ignore = true)
        })
        @Mapping(target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
        PersonDTO do2dto(PersonDO person);
    
        @Mappings({@Mapping(expression = "java(map.get(\"province\"))", target = "province"),
                @Mapping(expression = "java(map.get(\"city\"))", target = "city"),
                @Mapping(expression = "java(map.get(\"area\"))", target = "area"),
                @Mapping(expression = "java(map.get(\"street\"))", target = "street"),
                @Mapping(expression = "java(map.get(\"towns\"))", target = "towns"),
                @Mapping(expression = "java(map.get(\"community\"))", target = "community"),
                @Mapping(expression = "java(map.get(\"houseNums\"))", target = "houseNums")})
        HomeAddress map2HomeAddress(Map map);
    
    
        @Mappings(@Mapping(source = "userName", target = "name"))
        @Mapping(target = "address", expression = "java(homeAddressToString(dto2do.getAddress()))")
        PersonDO dto2do(PersonDTO dto2do);
    
        default String homeAddressToString(HomeAddress address) {
            return JSON.toJSONString(address);
        }
    
    }
    

    コードコンパイル後、PersonConverterImplが自動的に生成されます.
    package com.spring.security.demo.service;
    
    import com.spring.security.demo.Do.PersonDO;
    import com.spring.security.demo.base.GenderEnum;
    import com.spring.security.demo.dto.PersonDTO;
    import com.spring.security.demo.entity.HomeAddress;
    import java.util.Map;
    import javax.annotation.Generated;
    
    @Generated(
        value = "org.mapstruct.ap.MappingProcessor",
        date = "2021-01-07T11:33:24+0800",
        comments = "version: 1.0.0.Final, compiler: javac, environment: Java 1.8.0_251 (Oracle Corporation)"
    )
    public class PersonConverterImpl implements PersonConverter {
    
        @Override
        public PersonDTO do2dto(PersonDO person) {
            if ( person == null ) {
                return null;
            }
    
            PersonDTO personDTO = new PersonDTO();
    
            personDTO.setUserName( person.getName() );
            personDTO.setAge( person.getAge() );
            personDTO.setBirthday( person.getBirthday() );
            if ( person.getGender() != null ) {
                personDTO.setGender( Enum.valueOf( GenderEnum.class, person.getGender() ) );
            }
    
            return personDTO;
        }
    
        @Override
        public HomeAddress map2HomeAddress(Map map) {
            if ( map == null ) {
                return null;
            }
    
            HomeAddress homeAddress = new HomeAddress();
    
            homeAddress.setArea( map.get("area") );
            homeAddress.setProvince( map.get("province") );
            homeAddress.setCity( map.get("city") );
            homeAddress.setStreet( map.get("street") );
            homeAddress.setTowns( map.get("towns") );
            homeAddress.setHouseNums( map.get("houseNums") );
            homeAddress.setCommunity( map.get("community") );
    
            return homeAddress;
        }
    
        @Override
        public PersonDO dto2do(PersonDTO dto2do) {
            if ( dto2do == null ) {
                return null;
            }
    
            PersonDO personDO = new PersonDO();
    
            personDO.setName( dto2do.getUserName() );
            if ( dto2do.getAge() != null ) {
                personDO.setAge( dto2do.getAge() );
            }
            personDO.setBirthday( dto2do.getBirthday() );
            if ( dto2do.getGender() != null ) {
                personDO.setGender( dto2do.getGender().name() );
            }
    
            personDO.setAddress( homeAddressToString(dto2do.getAddress()) );
    
            return personDO;
        }
    }
    

    実行期間中にbeanをマッピングすると、PersonConverterImplのdto 2 doメソッドが直接呼び出され、特別なことは何もせず、メモリの中でsetやgetを行うだけでいいです.したがって,コンパイル期間中に多くのことをしたため,MapStructは実行期間中の性能が良好であり,問題の暴露をコンパイル期間に繰り上げることができるという利点もある.コード内のフィールドマッピングに問題がある場合、アプリケーションはコンパイルできず、開発者にこの問題を解決するように強制します.
    まとめ
    Javaのフィールドマッピングツールクラス、MapStructを紹介しました.彼の使い方は簡単で、機能が非常に完備していて、いろいろな状況のフィールドマッピングに対応することができます.また、コンパイル期間であるため、本格的なマッピングコードが生成され、実行期間のパフォーマンスが大幅に向上します.強くお勧めして、本当に香ばしい!!!