日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot对象拷贝

發(fā)布時(shí)間:2023/12/29 javascript 41 coder
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot对象拷贝 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄
  • 概述
  • 定義實(shí)體類
    • Car
    • size
    • carInfo
    • 造測試數(shù)據(jù)
  • Spring BeanUtils
  • Apache BeanUtils
  • Cglib BeanCopier
  • MapStruct
  • 性能測試
  • 深拷貝or淺拷貝

概述

眾所周知,java世界是由構(gòu)成的,各種各樣的類,提供各種各樣的作用,共同創(chuàng)造了一個(gè)個(gè)的java應(yīng)用。對(duì)象是類的實(shí)例,在SpringBoot框架中,對(duì)象經(jīng)常需要拷貝,例如數(shù)據(jù)庫實(shí)體拷貝成業(yè)務(wù)實(shí)體,導(dǎo)入實(shí)體轉(zhuǎn)換為業(yè)務(wù)實(shí)體,各種數(shù)據(jù)傳輸對(duì)象之間的拷貝等等。日常開發(fā)工作中用到的地方和頻率是相當(dāng)?shù)母摺1疚木蛧@對(duì)象拷貝來聊聊常用的姿勢(方式)和工具

定義實(shí)體類

為了演示對(duì)象拷貝將創(chuàng)建幾個(gè)實(shí)體類和幾個(gè)生成測試數(shù)據(jù)的方法。

Car

car描述了車輛這個(gè)業(yè)務(wù)對(duì)象, 其中包含一些常見的基本數(shù)據(jù)類型的屬性和一個(gè) size類型 的屬性,即包含基本數(shù)據(jù)類型和引用類型,包含子實(shí)體。


import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;

@Data
@Accessors(chain = true)
public class Car implements Serializable {
? ? private Integer id;
? ? private String name;
? ? private String brand;
? ? private String address;
? ? private Size size;
? ? private Double cc;
? ? /**
? ? ?* 扭矩
? ? ?*/
? ? private Double torque;
? ? /**
? ? ?* 廠商
? ? ?*/
? ? private String manufacturer;
? ? /**
? ? ?* 上市時(shí)間
? ? ?*/
? ? private LocalDate marketDate;
? ? /**
? ? ?* 售價(jià)
? ? ?*/
? ? private BigDecimal price;
}

size

size描述了車輛大小,具體來說就是長寬高。


import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;

@Data
@Accessors(chain = true)
public class Size ?implements Serializable {
? ? private Double length;
? ? private Double width;
? ? private Double height;
}

carInfo

car對(duì)象需要拷貝為carInfo對(duì)象,他們兩個(gè)大部分屬性都一樣,carInfo比car多了 color ,type


package com.ramble.demo.dto;
import com.ramble.demo.model.Size;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;

@Data
@Accessors(chain = true)
public class CarInfo implements Serializable {
? ? private Integer id;
? ? private String name;
? ? private String brand;
? ? private String address;
? ? private Size size;
? ? private Double cc;
? ? /**
? ? ?* 扭矩
? ? ?*/
? ? private Double torque;
? ? /**
? ? ?* 廠商
? ? ?*/
? ? private String manufacturer;
? ? /**
? ? ?* 上市時(shí)間
? ? ?*/
? ? private LocalDate marketDate;
? ? /**
? ? ?* 售價(jià)
? ? ?*/
? ? private BigDecimal price;
? ? private String color;
? ? private Integer type;
}

造測試數(shù)據(jù)

造測試數(shù)據(jù)用到了一個(gè)工具 javaFaker,通過實(shí)例化一個(gè)Faker 對(duì)象可以輕易的生成測試數(shù)據(jù):人名、地址、網(wǎng)址、手機(jī)號(hào)。。。。。。

gav坐標(biāo)為:


<dependency>
? ? <groupId>com.github.javafaker</groupId>
? ? <artifactId>javafaker</artifactId>
? ? <version>1.0.2</version>
</dependency>

造數(shù)據(jù)方法很簡單,一個(gè)批量一個(gè)單個(gè),可以通過count來控制造的數(shù)據(jù)量。


? ? /**
? ? ?* 批量造數(shù)據(jù)
? ? ?*
? ? ?* @return
? ? ?*/
? ? private List<Car> findCar() {
? ? ? ? Faker faker = new Faker(new Locale("zh-CN"));
? ? ? ? List<Car> list = new ArrayList<>();
? ? ? ? int count = 100000;
? ? ? ? for (int i = 0; i < count; i++) {
? ? ? ? ? ? list.add(getCar(faker));
? ? ? ? }
? ? ? ? return list;
? ? }
    
? ? /**
? ? ?* 造數(shù)據(jù) - 單個(gè)
? ? ?*
? ? ?* @param faker
? ? ?* @return
? ? ?*/
? ? private Car getCar(Faker faker) {
? ? ? ? Car car = new Car();
? ? ? ? car.setId(faker.number().numberBetween(1, 999999999));
? ? ? ? car.setName(faker.name().name());
? ? ? ? car.setBrand(faker.cat().breed());
? ? ? ? car.setAddress(faker.address().fullAddress());
? ? ? ? Size size = new Size();
? ? ? ? size.setLength(faker.number().randomDouble(7, 4000, 7000));
? ? ? ? size.setWidth(faker.number().randomDouble(7, 4000, 7000));
? ? ? ? size.setHeight(faker.number().randomDouble(7, 4000, 7000));
? ? ? ? car.setSize(size);
? ? ? ? car.setCc(faker.number().randomDouble(7, 1000, 12000));
? ? ? ? car.setTorque(faker.number().randomDouble(1, 100, 70000));
? ? ? ? car.setManufacturer(faker.name().name());
? ? ? ? Date date = faker.date().birthday();
? ? ? ? Instant instant = date.toInstant();
? ? ? ? ZoneId zone = ZoneId.systemDefault();
? ? ? ? LocalDate localDate = instant.atZone(zone).toLocalDate();
? ? ? ? car.setMarketDate(localDate);
? ? ? ? car.setPrice(BigDecimal.valueOf(faker.number().randomDigit()));
? ? ? ? return car;
? ? }


Spring BeanUtils

這么常用的功能官方肯定已經(jīng)集成了,對(duì)對(duì)對(duì),就是BeanUtils.copyProperties了。順便說一句,遇到找工具類的時(shí)候不要盲目baidu,不妨先看看springframework.util這個(gè)包下面的東西。

下面看看Spring的BeanUtils.copyProperties用法舉例


Faker faker = new Faker(new Locale("zh-CN"));
Car car = getCar(faker);
CarInfo carInfo = new CarInfo();
BeanUtils.copyProperties(car, carInfo);

  • 使用反射實(shí)現(xiàn)兩個(gè)對(duì)象的拷貝
  • 不會(huì)對(duì)類型進(jìn)行轉(zhuǎn)換,如source對(duì)象有一個(gè)integer類型的id屬性,target對(duì)象有一個(gè)string類型的id屬性,拷貝之后,target對(duì)象為null;同理integer 無法拷貝到long
  • 官方出品,無需引入其他依賴,推薦指數(shù)8顆星

Apache BeanUtils

apache-common系列的東西,java開發(fā)多多少少會(huì)用到一些,它本身也積累了很多經(jīng)驗(yàn)提供一些有用并常用的工具和組件。

針對(duì)BeanUtils 使用前需要引入pom


<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
<dependency>
? ? <groupId>commons-beanutils</groupId>
? ? <artifactId>commons-beanutils</artifactId>
? ? <version>1.9.4</version>
</dependency>

下面看看Apache的BeanUtils.copyProperties 用法舉例


Faker faker = new Faker(new Locale("zh-CN"));
Car car = getCar(faker);
CarInfo carInfo = new CarInfo();
try {
 org.apache.commons.beanutils.BeanUtils.copyProperties(carInfo, car);
} catch (IllegalAccessException e) {
? ? throw new RuntimeException(e);
} catch (InvocationTargetException e) {
? ? throw new RuntimeException(e);
}

  • 強(qiáng)制要處理異常
  • source對(duì)象和target對(duì)象相對(duì)Spring來說是顛倒的
  • 通過反射實(shí)現(xiàn)
  • 不會(huì)對(duì)類型進(jìn)行轉(zhuǎn)換,如source對(duì)象有一個(gè)integer類型的id屬性,target對(duì)象有一個(gè)string類型的id屬性,拷貝之后,target對(duì)象為null;同理integer 無法拷貝到long
  • 性能稍微遜色與Spring,推薦指數(shù)7顆星

Cglib BeanCopier

BeanCopier使用起來稍微有點(diǎn)復(fù)雜,需要先實(shí)例化一個(gè)BeanCopier對(duì)象,然后再調(diào)用其copy方法完成對(duì)象拷貝

使用前需要引入如下pom


<dependency>
? ? <groupId>cglib</groupId>
? ? <artifactId>cglib</artifactId>
? ? <version>3.2.0</version>
</dependency>

下面看看 Cglib 的 BeanCopier 用法舉例


Faker faker = new Faker(new Locale("zh-CN"));
Car car = getCar(faker);
CarInfo item = new CarInfo();
inal BeanCopier copier = BeanCopier.create(Car.class, CarInfo.class, false);
copier.copy(car, item, null);

  • BeanCopier雖然使用復(fù)雜,且要引入依賴,但是性能出眾
  • 對(duì)于數(shù)據(jù)量小的情況不沒有必要使用這個(gè),推薦指數(shù)5顆星
  • 對(duì)于數(shù)據(jù)量大,比如5W個(gè)對(duì)象拷貝為另外一個(gè)對(duì)象,這種場景雖然少但是一旦發(fā)生了Spring BeanUtils處理起來就非常耗時(shí),這種情況下推薦指數(shù)9顆星

MapStruct

MapStruct 是一個(gè)代碼生成器,它基于約定生成的映射代碼使用的是普通的方法調(diào)用,因此快速、類型安全且易于理解。MapStruct 本質(zhì)沒有太多科技與狠活,就是幫助開發(fā)人員編寫getA,setB這樣的樣板代碼,并提供擴(kuò)展點(diǎn),比如將carType映射為type。

使用前需要引入如下pom


<dependency>
? ? <groupId>org.mapstruct</groupId>
? ? <artifactId>mapstruct</artifactId>
? ? <version>1.5.0.Final</version>
</dependency>
<dependency>
? ? <groupId>org.mapstruct</groupId>
? ? <artifactId>mapstruct-processor</artifactId>
? ? <version>1.5.0.Final</version>
</dependency>

下面看看MapStruct用法舉例

使用前需要先定義一個(gè)接口,編寫轉(zhuǎn)換邏輯,而后通過調(diào)用接口方法獲取轉(zhuǎn)換后的結(jié)果。

CarInfoMapper


import com.ramble.demo.dto.CarInfo;
import com.ramble.demo.model.Car;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface CarInfoMapper {
? ? CarInfoMapper INSTANCT = Mappers.getMapper(CarInfoMapper.class);
? ? CarInfo carToCarInfo(Car car);
}

  • Mapper:將此接口標(biāo)注為用來轉(zhuǎn)換bean的接口,并在編譯過程中生成相應(yīng)的實(shí)現(xiàn)類
  • carToCarInfo,聲明一個(gè)轉(zhuǎn)換方法,并將source作為方法入?yún)ⅲ瑃arget作為方法出參
  • INSTANCT,按照約定,接口聲明一個(gè)INSTANCT成員,提供給訪問者消費(fèi)
  • 一個(gè)接口可以有多個(gè)轉(zhuǎn)換方法
  • 可通過在方法上添加@Mapping注解,將不同屬性名子的屬性進(jìn)行轉(zhuǎn)換
  • 可以將枚舉類型轉(zhuǎn)換為字符串
  • 性能出眾,術(shù)業(yè)有專攻,在處理復(fù)雜轉(zhuǎn)換和大數(shù)據(jù)量的時(shí)候很有必要
  • 因?yàn)橐胍蕾嚥⑶倚枰獑为?dú)編寫接口,所以對(duì)于簡單的轉(zhuǎn)換還是推薦Spring,這種情況下推薦指數(shù)5顆星
  • 如果在大數(shù)據(jù)量的情況,并且轉(zhuǎn)換相對(duì)復(fù)雜,推薦指數(shù)10顆星

調(diào)用 CarInfoMapper 進(jìn)行對(duì)象拷貝


Faker faker = new Faker(new Locale("zh-CN"));
Car car = getCar(faker);
CarInfo carInfo = CarInfoMapper.INSTANCT.carToCarInfo(car);

性能測試

原則上, 性能測試是多維度的,速度、CPU消耗、內(nèi)存消耗等等,本文僅考慮速度,只為說明問題,不為得到測試數(shù)據(jù)

測試代碼邏輯,使用for結(jié)合faker生成一個(gè)大的List,然后用上述4種方式做屬性拷貝。使用StopWatch記錄程序運(yùn)行時(shí)間。

測試代碼如下:


/**
?* 測試4種方式的處理速度
?*/
@GetMapping("/test1")
public void test1() {
? ? Faker faker = new Faker(new Locale("zh-CN"));
? ? List<Car> carList = findCar();
? ? StopWatch sw = new StopWatch("對(duì)象拷貝速度測試");
? ? sw.start("方式1:Spring BeanUtils.copyProperties");
? ? List<CarInfo> list1 = new ArrayList<>();
? ? carList.forEach(x -> {
? ? ? ? CarInfo item = new CarInfo();
? ? ? ? BeanUtils.copyProperties(x, item);
? ? ? ? item.setColor(faker.color().name());
? ? ? ? item.setType(faker.number().randomDigit());
? ? ? ? list1.add(item);
? ? });
? ? sw.stop();
? ? sw.start("方式2:apache BeanUtils.copyProperties");
? ? List<CarInfo> list2 = new ArrayList<>();
? ? carList.forEach(x -> {
? ? ? ? CarInfo item = new CarInfo();
? ? ? ? try {
? ? ? ? ? ? org.apache.commons.beanutils.BeanUtils.copyProperties(item, x);
? ? ? ? } catch (IllegalAccessException e) {
? ? ? ? ? ? throw new RuntimeException(e);
? ? ? ? } catch (InvocationTargetException e) {
? ? ? ? ? ? throw new RuntimeException(e);
? ? ? ? }
? ? ? ? item.setColor(faker.color().name());
? ? ? ? item.setType(faker.number().randomDigit());
? ? ? ? list2.add(item);
? ? });
? ? sw.stop();
? ? sw.start("方式3:BeanCopier");
? ? List<CarInfo> list3 = new ArrayList<>();
? ? carList.forEach(x -> {
? ? ? ? CarInfo item = new CarInfo();
? ? ? ? final BeanCopier copier = BeanCopier.create(Car.class, CarInfo.class, false);
? ? ? ? copier.copy(x, item, null);
? ? ? ? item.setColor(faker.color().name());
? ? ? ? item.setType(faker.number().randomDigit());
? ? ? ? list3.add(item);
? ? });
? ? sw.stop();
? ? sw.start("方式4:MapStruct");
? ? List<CarInfo> list4 = new ArrayList<>();
? ? carList.forEach(x -> {
? ? ? ? CarInfo item = CarInfoMapper.INSTANCT.carToCarInfo(x);
? ? ? ? item.setColor(faker.color().name());
? ? ? ? item.setType(faker.number().randomDigit());
? ? ? ? list4.add(item);
? ? });
? ? sw.stop();
? ? String s = sw.prettyPrint();
? ? System.out.println(s);
? ? double totalTimeSeconds = sw.getTotalTimeSeconds();
? ? System.out.println("總耗時(shí):" + totalTimeSeconds);
}

測試結(jié)果如下

10W 數(shù)據(jù)量


StopWatch '對(duì)象拷貝速度測試': running time = 5382233200 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
2336099000  043%  方式1:Spring BeanUtils.copyProperties
2322316400  043%  方式2:apache BeanUtils.copyProperties
442981700  008%  方式3:BeanCopier
280836100  005%  方式4:MapStruct

總耗時(shí):5.3822332

100W 數(shù)據(jù)量


StopWatch '對(duì)象拷貝速度測試': running time = 58689078300 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
25199532100  043%  方式1:Spring BeanUtils.copyProperties
27179965900  046%  方式2:apache BeanUtils.copyProperties
3629756500  006%  方式3:BeanCopier
2679823800  005%  方式4:MapStruct

總耗時(shí):58.6890783

深拷貝or淺拷貝

上述四種方式均為淺拷貝。可通過如下測試代碼驗(yàn)證


?/**
?* 測試4種方式是淺拷貝還是深拷貝
?*/
@GetMapping("/test2")
public void test2() {
? ? Faker faker = new Faker(new Locale("zh-CN"));
? ? Car car = getCar(faker);
? ? log.info("car={}", JSON.toJSONString(car));
? ? CarInfo carInfo = new CarInfo();
? ? //方式1
// ? ? ? ?BeanUtils.copyProperties(car, carInfo);
// ? ? ? ?Size sizeNew = car.getSize();
// ? ? ? ?sizeNew.setLength(0d);
// ? ? ? ?sizeNew.setWidth(0d);
// ? ? ? ?sizeNew.setHeight(0d);
//// ? ? ? ?Size s = new Size();
//// ? ? ? ?s.setHeight(10d);
//// ? ? ? ?car.setSize(s);
// ? ? ? ?car.setName("新名字");
// ? ? ? ?log.info("carInfo={}", JSON.toJSONString(carInfo));
? ? ? ? //方式2
// ? ? ? ?try {
// ? ? ? ? ? ?org.apache.commons.beanutils.BeanUtils.copyProperties(carInfo, car);
// ? ? ? ?} catch (IllegalAccessException e) {
// ? ? ? ? ? ?throw new RuntimeException(e);
// ? ? ? ?} catch (InvocationTargetException e) {
// ? ? ? ? ? ?throw new RuntimeException(e);
// ? ? ? ?}
// ? ? ? ?Size sizeNew = car.getSize();
// ? ? ? ?sizeNew.setLength(0d);
// ? ? ? ?sizeNew.setWidth(0d);
// ? ? ? ?sizeNew.setHeight(0d);
//
// ? ? ? ?car.setName("新名字");
// ? ? ? ?log.info("carInfo={}", JSON.toJSONString(carInfo));
? ? ? ? //方式3
// ? ? ? ?final BeanCopier copier = BeanCopier.create(Car.class, CarInfo.class, false);
// ? ? ? ?copier.copy(car, carInfo, null);
// ? ? ? ?Size sizeNew = car.getSize();
// ? ? ? ?sizeNew.setLength(0d);
// ? ? ? ?sizeNew.setWidth(0d);
// ? ? ? ?sizeNew.setHeight(0d);
//
// ? ? ? ?car.setName("新名字");
// ? ? ? ?log.info("carInfo={}", JSON.toJSONString(carInfo));
? ? ? ? //方式4
? ? ? ? carInfo = CarInfoMapper.INSTANCT.carToCarInfo(car);
? ? ? ? Size sizeNew = car.getSize();
? ? ? ? sizeNew.setLength(0d);
? ? ? ? sizeNew.setWidth(0d);
? ? ? ? sizeNew.setHeight(0d);
? ? ? ? car.setName("新名字");
? ? ? ? log.info("carInfo={}", JSON.toJSONString(carInfo));
}

驗(yàn)證邏輯為,在完成copy后修改car.size 的值,發(fā)現(xiàn)carInfo.size隨之改變,說明是淺拷貝。

總結(jié)

以上是生活随笔為你收集整理的SpringBoot对象拷贝的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。