欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java對象轉(zhuǎn)換的方案分享

 更新時間:2022年09月13日 08:56:50   作者:Real_man???????  
這篇文章主要介紹了Java對象轉(zhuǎn)換的方案分享,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下

前言

系統(tǒng)變的復雜,系統(tǒng)的層次劃分越來越細,邊界也越來越明確。 然后每一層之間一般都有自己要處理的領域?qū)ο?,統(tǒng)稱為pojo一般在model或者domain包下(類的后綴不能為pojo)。

常見的一些模型類型:

  • PO、DO:持久層對象,一般和數(shù)據(jù)庫直接打交道。
  • DTO:數(shù)據(jù)傳輸對象,系統(tǒng)之間的交互,再服務層提供服務的時候輸出到其它系統(tǒng)。
  • VO:視圖對象,用于前端模型展示。 當然有時候前端也可以看做另外一個系統(tǒng),使用DTO模型;
  • BO:業(yè)務邏輯對象,比較少用...

為什么模型要分這么多層?

在復雜一點的業(yè)務中,業(yè)務建模是非常有必要的,一定要抽象出業(yè)務上常用的領域模型,統(tǒng)一技術(shù)和非技術(shù)同學的語言。

建完模型之后,在技術(shù)的系統(tǒng)中,為了方便維護代碼,分離關(guān)注點,也會進行再次分層,讓每一層解決特定的問題。模型的分層是隨著系統(tǒng)的分層而來的;試想所有的模型屬性在一個對象中,這個對象你看的懂嗎?

舉個實際的案例:

  • 數(shù)據(jù)層一般用DO
  • 現(xiàn)在要透出數(shù)據(jù)給其他系統(tǒng),DO中一般都會有創(chuàng)建人,創(chuàng)建時間,修改人,修改時間,當前對象所處的環(huán)境等信息; 但是外部的系統(tǒng)需要環(huán)境、創(chuàng)建人信息嗎? 很多時候不需要,站在數(shù)據(jù)安全的角度,一般只透出必要的字段就可以; 這些要輸出要外部系統(tǒng)的必要字段,一般定義在DTO中。
  • 到前端系統(tǒng),前端系統(tǒng)展示上所需的邏輯和輸出到外部系統(tǒng)的又有點不太一樣,前端系統(tǒng)可能要創(chuàng)建人,創(chuàng)建時間,但是不要另外一些東西,或者一些敏感的配置不能透出給前端,這個時候一般也會再定義一個新的對象。

簡單說就是當我們的系統(tǒng)要輸出能力到外部系統(tǒng)的時候,不同系統(tǒng)要的數(shù)據(jù)不一樣,數(shù)據(jù)安全要求我們不能透出這么多的數(shù)據(jù),一定要做一層處理。 另外給另外一個系統(tǒng)關(guān)注的數(shù)據(jù),而不是一股腦的全部都給對方,對方處理起來也方便。

模型之間的轉(zhuǎn)換

建議不要用的方式

  • 手寫get\set; 雖然性能高,但是費勁并且眼花繚亂,一不小心就寫錯了,難以維護,復用度不高
  • BeanUtils,apacha和spring包下都有對應的類,但是底層用到的都是反射,性能比較差,大流量的情況下一般不用
  • 直接fastjson,gc會很頻繁,而且性能比較差

常用的方式

  • cglib的beanCopier,開銷在創(chuàng)建BeanCopier,一般在創(chuàng)建類的時候提前創(chuàng)建好一個,在代碼運行的時候直接進行copy,性能接近原生。
  • mapstruct 性能和原生代碼一樣,支持復雜的轉(zhuǎn)化場景,實現(xiàn)原理同lombok編譯的時候生成對應的代碼。

以上從技術(shù)分類的角度來看:

  • 反射:fastjson,beanutil 都不建議用
  • get\set: beancoper通過字節(jié)碼進行g(shù)etset,mapstruct編譯的時候生成getset。 性能相對較好。

使用方式

個人覺得,如果說對象比較簡單的時候,使用BeanCopier就可以了,因為spring的aop依賴cglib,默認情況下就已經(jīng)引入了對應的包了,不需要額外的依賴直接就可以用。

如果很復雜的模型之間的轉(zhuǎn)換,并且對性能有更極致的要求,考慮使用下MapStruct。

定義對象

UserDO

@Data
public class UserDO {
  private Long id;
  private String name;
  private Integer gender;
  private String password;
  private Date gmtCreate;
  private Date gmtModified;
}

UserDTO

@Data
public class UserDTO {
  private Long id;
  private String name;
  private Integer gender;
}

BeanCopier

最簡單的使用方式

BeanCopier beanCopier = BeanCopier.create(UserDO.class, UserDTO.class, false); bean.copy即可;

private static void simpleBeanCopy() {
    BeanCopier beanCopier = BeanCopier.create(UserDO.class, UserDTO.class, false);
    UserDO userDO = new UserDO();
    userDO.setId(1L);
    userDO.setName("aihe");
    userDO.setGmtCreate(new Date());
    userDO.setGender(0);
    userDO.setPassword("xxxxxx");
    UserDTO userDTO = new UserDTO();
    beanCopier.copy(userDO, userDTO,null);
    Assert.assertEquals("名稱未成功拷貝",userDTO.getName(),"aihe");
    Assert.assertEquals("Id未成功拷貝", 1L, (long)userDTO.getId());
    Assert.assertEquals("性別未成功拷貝", Integer.valueOf(0),userDTO.getGender());
  }

創(chuàng)建可復用的BeanCopier工具類

package me.aihe.daka;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.sf.cglib.beans.BeanCopier;
/**
 * @author : aihe 
 * @date : 2022/9/12 9:21 AM
 * 使用場景:
 * 功能描述:
 */
public class BeanCopyUtils {

    /**
     * beanCopier緩存
     * 由sourceClass和targetClass可以確定一個唯一的BeanCoper,因此使用二級Map;
     */
    private static Map<Class<?>, Map<Class<?>, BeanCopier>> beanCopierMap = new ConcurrentHashMap<>();
    /**
     * 直接指定Bean對象進行拷貝
     * @param sourceBean
     * @param targetBean
     * @param <S>
     * @param <T>
     */
    public static <S,T> void copy(S sourceBean,T targetBean){
        @SuppressWarnings("unchecked")
        Class<S> sourceClass = (Class<S>) sourceBean.getClass();
        @SuppressWarnings("unchecked")
        Class<T> targetClass = (Class<T>) targetBean.getClass();

        BeanCopier beanCopier = getBeanCopier(sourceClass,targetClass);
        beanCopier.copy(sourceBean,targetBean,null);
    }
    /**
     * 轉(zhuǎn)換方法
     * @param sourceBean 原對象
     * @param targetClass 目標類
     * @param <S>
     * @param <T>
     * @return
     */
    public static <S,T> T convert(S sourceBean,Class<T> targetClass){
        try {
            assert sourceBean!=null;
            T targetBean = targetClass.newInstance();
            copy(sourceBean,targetBean);
            return targetBean;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    private static <S,T> BeanCopier getBeanCopier(Class<S> sourceClass, Class<T> targetClass ){
        Map<Class<?>,BeanCopier> map = beanCopierMap.get(sourceClass);
        if(map == null || map.isEmpty()){
            BeanCopier newBeanCopier = BeanCopier.create(sourceClass, targetClass, false);
            Map<Class<?>,BeanCopier> newMap = new ConcurrentHashMap<>();
            newMap.put(targetClass,newBeanCopier);
            beanCopierMap.put(sourceClass,newMap);
            return newBeanCopier;
        }
        BeanCopier beanCopier = map.get(targetClass);
        if(beanCopier == null){
            BeanCopier newBeanCopier = BeanCopier.create(sourceClass, targetClass, false);
            map.put(targetClass,newBeanCopier);

            return newBeanCopier;
        }
        return beanCopier;
    }
}

同上:

UserDO userDO = new UserDO();
    userDO.setId(1L);
    userDO.setName("aihe");
    userDO.setGmtCreate(new Date());
    userDO.setGender(0);
    userDO.setPassword("xxxxxx");
    UserDTO userDTO = new UserDTO();
    BeanCopyUtils.copy(userDO, userDTO);
    Assert.assertEquals("名稱未成功拷貝",userDTO.getName(),"aihe");
    Assert.assertEquals("Id未成功拷貝", 1L, (long)userDTO.getId());
    Assert.assertEquals("性別未成功拷貝", Integer.valueOf(0),userDTO.getGender());

MapStruct

案例集:github.com/mapstruct/m…

引入mapstruct

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <org.mapstruct.version>1.5.2.Final</org.mapstruct.version>
        <org.projectlombok.version>1.18.20</org.projectlombok.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${org.mapstruct.version}</version>
            <!-- IntelliJ does not pick up the processor if it is not in the dependencies.
             There is already an open issue for IntelliJ see https://youtrack.jetbrains.com/issue/IDEA-150621
            -->
            <scope>provided</scope>
        </dependency>
       
         <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${org.projectlombok.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

簡單Demo

定義Mapper

@Mapper
public interface UserDTOMapper {

    UserDTOMapper MAPPER = Mappers.getMapper( UserDTOMapper.class );
    //@Mapping( source = "test", target = "testing" )
    //@Mapping( source = "test1", target = "testing2" )
    UserDTO toTarget( UserDO s );
}

使用:

public static void main( String[] args ) {
        //simpleDemo();
        UserDO userDO = new UserDO();
        userDO.setId(1L);
        userDO.setName("aihe");
        userDO.setGmtCreate(new Date());
        userDO.setGender(0);
        userDO.setPassword("xxxxxx");
        UserDTO userDTO = UserDTOMapper.MAPPER.toTarget(userDO);
        Assert.assertEquals("名稱未成功拷貝",userDTO.getName(),"aihe");
        Assert.assertEquals("Id未成功拷貝", 1L, (long)userDTO.getId());
        Assert.assertEquals("性別未成功拷貝", Integer.valueOf(0),userDTO.getGender());
    }

常見用法

  • 屬性類型相同,名稱不同的時候,使用@Mapping注解指定source和target字段名稱對應關(guān)系, 如果有多個這種屬性,那就指定多個@Mapping注解。
  • 忽略某個字段,在@Mapping的時候,加上ignore = true
  • 轉(zhuǎn)化日期格式,字符串到數(shù)字的格式,可以使用dateFormat,numberFormat
  • 如果有自定義轉(zhuǎn)換的需求,寫一個簡單的Java類即可,然后在方法上打上Mapstruct的注解@Named,在在@Mapper(uses = 自定義的類),然后@Mapping中用上qualifiedByName。
@Mapping(target = "userNick1", source = "userNick")
@Mapping(target = "createTime", source = "createTime", dateFormat = "yyyy-MM-dd")
@Mapping(target = "age", source = "age", numberFormat = "#0.00")
@Mapping(target = "id", ignore = true)
@Mapping(target = "userVerified", defaultValue = "defaultValue-2")
UserDTO toTarget( UserDO s );

性能測試

測試代碼

import java.util.Date;
import com.alibaba.fastjson.JSON;
import org.junit.Before;
import org.junit.Test;
/**
 * @author : aihe aihe.ah@alibaba-inc.com
 * @date : 2022/9/12 9:47 AM
 * 使用場景:
 * 功能描述:
 */
public class BenchDemoTest{

  /**
   * 轉(zhuǎn)化對象
   */
  private UserDO userDO;

  /**
   * 轉(zhuǎn)化次數(shù)
   */
  private final static int count = 1000000;
  @Before
  public void before() {
    userDO = new UserDO();
    userDO.setId(1L);
    userDO.setName("aihe");
    userDO.setGmtCreate(new Date());
    userDO.setGender(0);
    userDO.setPassword("xxxxxx");
  }
  @Test
  public void mapstruct() {
    long startTime = System.currentTimeMillis();
    for (int i = 1; i <=count; i++) {
      UserDTO userDTO = UserDTOMapper.MAPPER.toTarget(userDO);
    }
    System.out.println("mapstruct time" + (System.currentTimeMillis() - startTime));
  }
  @Test
  public void beanCopier() {
    long startTime = System.currentTimeMillis();
    for (int i = 1; i <= count; i++) {
      UserDTO targetBean = new UserDTO();
      BeanCopyUtils.copy(userDO, targetBean);
    }
    System.out.println("beanCopier time" + (System.currentTimeMillis() - startTime));
  }
  @Test
  public void springBeanUtils(){
    long startTime = System.currentTimeMillis();
    for (int i = 1; i <=count; i++) {
      UserDTO userDTO = new UserDTO();
      org.springframework.beans.BeanUtils.copyProperties(userDO, userDTO);
    }
    System.out.println("springBeanUtils time" + (System.currentTimeMillis() - startTime));
  }
  @Test
  public void fastjson() {
    long startTime = System.currentTimeMillis();
    for (int i = 1; i <= count; i++) {
      JSON.parseObject(JSON.toJSONString(userDO), UserDTO.class);
    }
    System.out.println("fastjson time" + (System.currentTimeMillis() - startTime));
  }
}

測試結(jié)果

  • 可以看出BeanCopier和MapStruct是遠遠超過其他轉(zhuǎn)換方式的...
  • BeanCopier雖然快,但是比mapstruct還是有20倍的性能差距...

最后

總結(jié)下本文的內(nèi)容:

  • 軟件系統(tǒng)一般都會進行分層,領域模型也會隨之進行分層,即每層都有自己關(guān)注的模型對象; 分層的主要原因是便于維護。
  • 模型之間的對象經(jīng)常要互相轉(zhuǎn)換,常用的轉(zhuǎn)換實現(xiàn)有反射和get/set,反射的性能很差不建議使用
  • 然后寫了基于get/set實現(xiàn)的beancopier和mapstruct使用方式,簡單測試了下性能,mapstrcut優(yōu)于其它各種對象轉(zhuǎn)換方式。并且MapStrcut支持功能更加復雜的對象轉(zhuǎn)換。 性能又好,功能又強大,所以可以考慮優(yōu)先使用.

到此這篇關(guān)于Java對象轉(zhuǎn)換的方案分享的文章就介紹到這了,更多相關(guān)Java對象轉(zhuǎn)換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java微信公眾號開發(fā)之通過微信公眾號獲取用戶信息

    Java微信公眾號開發(fā)之通過微信公眾號獲取用戶信息

    這篇文章主要介紹了Java微信公眾號開發(fā)之通過微信公眾號獲取用戶信息,需要的朋友可以參考下
    2017-05-05
  • SpringCloud微服務架構(gòu)升級匯總

    SpringCloud微服務架構(gòu)升級匯總

    這篇文章主要介紹了SpringCloud微服務架構(gòu)升級匯總,它提倡將單一應用程序劃分成一組小的服務,服務之間互相協(xié)調(diào)、互相配合,為用戶提供最終價值,需要的朋友可以參考下
    2019-06-06
  • Spring Aop 源碼增強獲取分享

    Spring Aop 源碼增強獲取分享

    這篇文章主要介紹了Spring Aop 源碼增強獲取分享,文章圍繞主題的內(nèi)容展開Spring Aop的相關(guān)介紹,具有一定的參考價值需要的小伙伴可以參考一下
    2022-05-05
  • Java線程池ForkJoinPool實例解析

    Java線程池ForkJoinPool實例解析

    這篇文章主要介紹了Java線程池ForkJoinPool實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02
  • 數(shù)據(jù)庫基本操作語法歸納總結(jié)

    數(shù)據(jù)庫基本操作語法歸納總結(jié)

    本篇文章主要介紹了數(shù)據(jù)庫的一些常用方法及一些基本操作,需要的朋友可以參考下
    2017-04-04
  • SpringBoot詳細講解視圖整合引擎thymeleaf

    SpringBoot詳細講解視圖整合引擎thymeleaf

    這篇文章主要分享了Spring Boot整合使用Thymeleaf,Thymeleaf是新一代的Java模板引擎,類似于Velocity、FreeMarker等傳統(tǒng)引擎,關(guān)于其更多相關(guān)內(nèi)容,需要的小伙伴可以參考一下
    2022-06-06
  • java 定時器Timer和TimerTask的使用詳解(執(zhí)行和暫停)

    java 定時器Timer和TimerTask的使用詳解(執(zhí)行和暫停)

    這篇文章主要介紹了java 定時器Timer和TimerTask的使用詳解(執(zhí)行和暫停),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-11-11
  • Java中的15種鎖

    Java中的15種鎖

    在讀很多并發(fā)文章中,會提及各種各樣鎖如公平鎖,樂觀鎖等等,這篇文章小編將向大家介紹是各種鎖的分類,感興趣的小伙伴可以參考下面文章的具體內(nèi)容
    2021-09-09
  • Java語言ReadWriteLock特性實例測試

    Java語言ReadWriteLock特性實例測試

    這篇文章主要介紹了Java語言ReadWriteLock特性實例測試,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-02-02
  • 詳解SpringBoot通過restTemplate實現(xiàn)消費服務

    詳解SpringBoot通過restTemplate實現(xiàn)消費服務

    本篇文章主要介紹了詳解使用RestTemplate消費spring boot的Restful服務,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01

最新評論