Spring的BeanUtils.copyProperties()与MapStruct 都是浅拷贝,BeanUtils.copyProperties()底层使用的是反射拷贝,而MapStruct底层方法是使用get(),set()拷贝,故速度比Spring的copy要快上不少
与Spring.BeanUtils.copy(source,target)对比结果图:
拷贝对象:TradeOrder 10 - 20 个有值字段进行拷贝
从图上来看,100000次拷贝速度,set > mapStruct > BeanCopier.copy > BeanUtils.copyProperties
使用步骤
1、引入依赖
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.3.Final</version>
</dependency>
⚠️:以下所有Mapper相关,都是MapStruct 包下的类(易与mybatis的Mapper混淆),便于区分可以在声明接口以其他标识作为后缀
2、调用方式
2.1 Mapper工厂:声明一个接口,接口上加上@Mapper 注解 ,Mapper实例可以通过mapStruct 包下的Mappers类来获取,调用getMapper()方法
声明:
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface MapStructMapper {
MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class);
TradeOrder convert(TradeOrder tradeOrder);
Test2 convert(Test1 test1);
TestDO convert(TestDO testDO);
}
2.2 Spring 注入:需要在声明的拷贝接口上的注解加上componentModel = "spring"的参数(@Mapper(componentModel = "spring")),即可注册为Spring Bean对象,使用的时候注入使用即可
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring")
public interface MapStructMapper {
MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class);
TradeOrder convert(TradeOrder tradeOrder);
Test2 convert(Test1 test1);
TestDO convert(TestDO testDO);
}
3、基本原理:被@Mapper声明的接口,在编译期会生成impl文件,该文件的内容为声明方法的拷贝,内部都是使用get()/set()方法实现
例:声明方法TradeOredr copyTradeOrderSelf(TradeOrder tradeOrder);
入参就是拷贝对象(等效于BeanUtils.copyProperties中 source),返回参数为拷贝后对象(等效于BeanUtils.copyProperties中 target)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.xcode.server.common.mapstruct;
import com.xcode.server.common.base.TestDO;
import com.xcode.server.common.base.TradeOrder;
import com.xcode.server.entity.ProductDO;
import com.xcode.server.entity.Test1;
import com.xcode.server.entity.Test2;
import java.util.ArrayList;
import java.util.List;
public class MapStructMapperImpl implements MapStructMapper {
public MapStructMapperImpl() {
}
public TradeOrder convert(TradeOrder tradeOrder) {
if (tradeOrder == null) {
return null;
} else {
TradeOrder tradeOrder1 = new TradeOrder();
tradeOrder1.setId(tradeOrder.getId());
tradeOrder1.setTradeId(tradeOrder.getTradeId());
tradeOrder1.setSourceTradeId(tradeOrder.getSourceTradeId());
tradeOrder1.setTradeNo(tradeOrder.getTradeNo());
tradeOrder1.setTradeFrom(tradeOrder.getTradeFrom());
tradeOrder1.setTradeStatus(tradeOrder.getTradeStatus());
tradeOrder1.setProcessStatus(tradeOrder.getProcessStatus());
tradeOrder1.setTradeTime(tradeOrder.getTradeTime());
tradeOrder1.setPayTime(tradeOrder.getPayTime());
tradeOrder1.setAuditTime(tradeOrder.getAuditTime());
tradeOrder1.setReviewTime(tradeOrder.getReviewTime());
tradeOrder1.setNotifyPickTime(tradeOrder.getNotifyPickTime());
tradeOrder1.setConsignTime(tradeOrder.getConsignTime());
tradeOrder1.setConfirmTime(tradeOrder.getConfirmTime());
tradeOrder1.setOrderNo(tradeOrder.getOrderNo());
tradeOrder1.setShopId(tradeOrder.getShopId());
tradeOrder1.setShopName(tradeOrder.getShopName());
tradeOrder1.setCompanyId(tradeOrder.getCompanyId());
tradeOrder1.setCompanyName(tradeOrder.getCompanyName());
tradeOrder1.setDepartId(tradeOrder.getDepartId());
tradeOrder1.setDepartName(tradeOrder.getDepartName());
tradeOrder1.setWarehouseId(tradeOrder.getWarehouseId());
tradeOrder1.setWarehouseName(tradeOrder.getWarehouseName());
tradeOrder1.setLogisticId(tradeOrder.getLogisticId());
tradeOrder1.setLogisticName(tradeOrder.getLogisticName());
tradeOrder1.setLogisticType(tradeOrder.getLogisticType());
tradeOrder1.setMainPostid(tradeOrder.getMainPostid());
tradeOrder1.setTradeType(tradeOrder.getTradeType());
tradeOrder1.setTotalFee(tradeOrder.getTotalFee());
tradeOrder1.setTaxFee(tradeOrder.getTaxFee());
tradeOrder1.setReceivedPostFee(tradeOrder.getReceivedPostFee());
tradeOrder1.setDiscountFee(tradeOrder.getDiscountFee());
tradeOrder1.setPayment(tradeOrder.getPayment());
tradeOrder1.setCouponFee(tradeOrder.getCouponFee());
tradeOrder1.setReceivedTotal(tradeOrder.getReceivedTotal());
tradeOrder1.setPostFee(tradeOrder.getPostFee());
tradeOrder1.setOtherFee(tradeOrder.getOtherFee());
tradeOrder1.setIsMultiChargeType(tradeOrder.getIsMultiChargeType());
tradeOrder1.setChargeType(tradeOrder.getChargeType());
tradeOrder1.setChargeCurrency(tradeOrder.getChargeCurrency());
tradeOrder1.setChargeAccount(tradeOrder.getChargeAccount());
tradeOrder1.setAccountName(tradeOrder.getAccountName());
tradeOrder1.setPayType(tradeOrder.getPayType());
tradeOrder1.setPayAccount(tradeOrder.getPayAccount());
tradeOrder1.setPayNo(tradeOrder.getPayNo());
tradeOrder1.setSellerMemo(tradeOrder.getSellerMemo());
tradeOrder1.setBuyerMemo(tradeOrder.getBuyerMemo());
tradeOrder1.setAppendMemo(tradeOrder.getAppendMemo());
tradeOrder1.setRegister(tradeOrder.getRegister());
tradeOrder1.setRegisterId(tradeOrder.getRegisterId());
tradeOrder1.setSellerId(tradeOrder.getSellerId());
tradeOrder1.setSeller(tradeOrder.getSeller());
tradeOrder1.setAuditorId(tradeOrder.getAuditorId());
tradeOrder1.setAuditor(tradeOrder.getAuditor());
tradeOrder1.setReviewerId(tradeOrder.getReviewerId());
tradeOrder1.setReviewer(tradeOrder.getReviewer());
tradeOrder1.setEstimateWeight(tradeOrder.getEstimateWeight());
tradeOrder1.setPackageWeight(tradeOrder.getPackageWeight());
tradeOrder1.setTradeCount(tradeOrder.getTradeCount());
tradeOrder1.setGoodsTypeCount(tradeOrder.getGoodsTypeCount());
tradeOrder1.setIsDeliveryBatch(tradeOrder.getIsDeliveryBatch());
tradeOrder1.setIsFreeze(tradeOrder.getIsFreeze());
tradeOrder1.setFreezeReason(tradeOrder.getFreezeReason());
tradeOrder1.setIsAbnormal(tradeOrder.getIsAbnormal());
tradeOrder1.setAbnormalDescription(tradeOrder.getAbnormalDescription());
tradeOrder1.setGoodslist(tradeOrder.getGoodslist());
tradeOrder1.setPrice(tradeOrder.getPrice());
tradeOrder1.setIsDelete(tradeOrder.getIsDelete());
tradeOrder1.setSettleStatus(tradeOrder.getSettleStatus());
tradeOrder1.setGmtCreate(tradeOrder.getGmtCreate());
tradeOrder1.setGmtModified(tradeOrder.getGmtModified());
tradeOrder1.setStockoutNo(tradeOrder.getStockoutNo());
tradeOrder1.setFlagIds(tradeOrder.getFlagIds());
tradeOrder1.setSysFlagIds(tradeOrder.getSysFlagIds());
tradeOrder1.setLastShipTime(tradeOrder.getLastShipTime());
tradeOrder1.setPayStatus(tradeOrder.getPayStatus());
tradeOrder1.setChargeCurrencyCode(tradeOrder.getChargeCurrencyCode());
tradeOrder1.setChargeExchangeRate(tradeOrder.getChargeExchangeRate());
tradeOrder1.setGrossProfit(tradeOrder.getGrossProfit());
tradeOrder1.setEstimateVolume(tradeOrder.getEstimateVolume());
tradeOrder1.setCancelReason(tradeOrder.getCancelReason());
tradeOrder1.setCommision(tradeOrder.getCommision());
tradeOrder1.setOnlineTradeNo(tradeOrder.getOnlineTradeNo());
tradeOrder1.setPayDueDate(tradeOrder.getPayDueDate());
tradeOrder1.setLocalExchangeRate(tradeOrder.getLocalExchangeRate());
tradeOrder1.setLocalCurrencyCode(tradeOrder.getLocalCurrencyCode());
tradeOrder1.setArriveTime(tradeOrder.getArriveTime());
tradeOrder1.setIsManualCompleteDelivery(tradeOrder.getIsManualCompleteDelivery());
tradeOrder1.setJmallAccountName(tradeOrder.getJmallAccountName());
tradeOrder1.setCustomerPayment(tradeOrder.getCustomerPayment());
tradeOrder1.setCustomerDiscountFee(tradeOrder.getCustomerDiscountFee());
tradeOrder1.setCustomerPostFee(tradeOrder.getCustomerPostFee());
tradeOrder1.setCustomerTotalFee(tradeOrder.getCustomerTotalFee());
tradeOrder1.setLogisticStatus(tradeOrder.getLogisticStatus());
tradeOrder1.setIsUploadMart(tradeOrder.getIsUploadMart());
tradeOrder1.setExpectReachTime(tradeOrder.getExpectReachTime());
tradeOrder1.setDistributeLimitation(tradeOrder.getDistributeLimitation());
tradeOrder1.setCustomerPayNo(tradeOrder.getCustomerPayNo());
tradeOrder1.setCustomerTaxFee(tradeOrder.getCustomerTaxFee());
tradeOrder1.setCustomerShopId(tradeOrder.getCustomerShopId());
tradeOrder1.setInsuredValue(tradeOrder.getInsuredValue());
tradeOrder1.setDistributionStationId(tradeOrder.getDistributionStationId());
tradeOrder1.setDistributionStation(tradeOrder.getDistributionStation());
tradeOrder1.setPickupTime(tradeOrder.getPickupTime());
tradeOrder1.setPickupTimeId(tradeOrder.getPickupTimeId());
tradeOrder1.setMerchandiserId(tradeOrder.getMerchandiserId());
tradeOrder1.setMerchandiser(tradeOrder.getMerchandiser());
tradeOrder1.setSourceTradeType(tradeOrder.getSourceTradeType());
tradeOrder1.setApiType(tradeOrder.getApiType());
tradeOrder1.setSalesId(tradeOrder.getSalesId());
tradeOrder1.setSalesName(tradeOrder.getSalesName());
tradeOrder1.setPickUpTime(tradeOrder.getPickUpTime());
tradeOrder1.setSigningTime(tradeOrder.getSigningTime());
tradeOrder1.setWarehouseFee(tradeOrder.getWarehouseFee());
tradeOrder1.setCompleteTime(tradeOrder.getCompleteTime());
tradeOrder1.setMemberId(tradeOrder.getMemberId());
tradeOrder1.setMemberName(tradeOrder.getMemberName());
tradeOrder1.setInTransitTime(tradeOrder.getInTransitTime());
tradeOrder1.setDeliveryingTime(tradeOrder.getDeliveryingTime());
tradeOrder1.setTestDO(this.convert(tradeOrder.getTestDO()));
return tradeOrder1;
}
}
public Test2 convert(Test1 test1) {
if (test1 == null) {
return null;
} else {
Test2 test2 = new Test2();
test2.setAsda(test1.getAsda());
if (test1.getTrueStr() != null) {
test2.setTrueStr(Boolean.parseBoolean(test1.getTrueStr()));
}
test2.setAaa(test1.isAaa());
test2.setBbb(test1.getBbb());
return test2;
}
}
public TestDO convert(TestDO testDO) {
if (testDO == null) {
return null;
} else {
TestDO testDO1 = new TestDO();
testDO1.setYes(testDO.isYes());
testDO1.setName(testDO.getName());
testDO1.setStock(testDO.getStock());
testDO1.setTt(testDO.getTt());
testDO1.setProductDO(testDO.getProductDO());
testDO1.setIsDelete(testDO.getIsDelete());
testDO1.setTrue(testDO.isTrue());
List<ProductDO> list = testDO.getProductDOs();
if (list != null) {
testDO1.setProductDOs(new ArrayList(list));
}
return testDO1;
}
}
}
如果需要映射不同字段的:需要在Mapper接口方法上使用@Mapping(source = "fieldName", target = "targetName")即可,例:
@Mapping(source = "field2", target = "field1")
TradeOrder convert(TradeOrder tradeOrder);
⚠️需注意点:存在类型从大转到小会丢失精度问题,需要在方法上添加参数 typeConversionPolicy = ReportingPolicy.ERROR
@Mapper(componentModel = "spring", typeConversionPolicy = ReportingPolicy.ERROR)
public interface MapStructCopyComponent {
}
这样在编译期间如果发现该种字段,会编译报错,如果一定要两个拷贝的对象进行编译成功,可以在具体方法上添加注解,忽略对象拷贝
@Mappings(
{@Mapping(target = "strLong", ignore = true),
@Mapping(target = "stock", ignore = true)}
)
TestDto convert11(TestDO testDO);
String <-> Integer/Long/Byte 等,会进行隐式转换,
例“a123“ 转成 Integer/Long/Byte 会直接报错
目前没有什么方法进行避免,转换错会抛出异常