目录
1、性能分析
在我们平时开发过程中,往往会涉及到,对象和对象直接的转化,而看到这个需求时直接想到的就是反射和BeanUtils工具,但是这个之间的效率往往是非常低的。
/**
* @author Bill
* @date Created in 2021/9/28 15:59 <br>
*/
public class ReflectTest {
/**
* 实体转换次数
*/
private static final int LIMIT_NUM = 1;
/**
* 反射性能分析
*/
@Test
public void beanUtil() {
long start = System.currentTimeMillis();
OrderDTO orderDto = new OrderDTO();
orderDto.setName("mapstruct 测试");
orderDto.setCost(12L);
orderDto.setPrice("13");
orderDto.setWeight(123L);
orderDto.setIsDeleted(false);
orderDto.setIsFinish(true);
orderDto.setGmtCreat(LocalDateTime.now());
orderDto.setGmtModified(LocalDateTime.now());
ItemDTO itemDTO = new ItemDTO();
itemDTO.setItemName("衬衫");
itemDTO.setPrice(12L);
orderDto.setItemList(Arrays.asList(itemDTO));
for (int i = 0; i < LIMIT_NUM; i++) {
final Order order = new Order();
BeanUtils.copyProperties(orderDto, order);
}
long end = System.currentTimeMillis();
System.out.println("BeanUtils耗时:"+(end - start ) + "ms");
mapstruct();
}
/**
* 反射性能分析
*/
@Test
public void mapstruct() {
long start = System.currentTimeMillis();
OrderDTO orderDto = new OrderDTO();
orderDto.setName("mapstruct 测试");
orderDto.setCost(12L);
orderDto.setPrice("13");
orderDto.setWeight(123L);
orderDto.setIsDeleted(false);
orderDto.setIsFinish(true);
orderDto.setGmtCreat(LocalDateTime.now());
orderDto.setGmtModified(LocalDateTime.now());
ItemDTO itemDTO = new ItemDTO();
itemDTO.setItemName("衬衫");
itemDTO.setPrice(12L);
orderDto.setItemList(Arrays.asList(itemDTO));
for (int i = 0; i < LIMIT_NUM; i++) {
final Order order = new Order();
OrderMapper.INSTANCE.updateOrder(orderDto, order);
}
long end = System.currentTimeMillis();
System.out.println("mapstruct耗时:"+(end - start ) + "ms");
}
}
平均结果:
BeanUtils耗时:150ms
mapstruct耗时:10ms
从上面可以看出BeanUtils和Mapstruct的性能差距还是蛮大的。
当然很多人也会说放射如果通过缓存性能能极大的提升,但是问题是,大部分的情况下我们是无法满足缓存的条件,而且这种反射往往比较麻烦,所以今天来看看Mapstruct这个技术。
2、什么是Mapstruct
原文
MapStruct is a code generator that greatly simplifies the implementation of mappings between Java bean types based on a convention over configuration approach.
The generated mapping code uses plain method invocations and thus is fast, type-safe and easy to understand.
Mapsturct 是一个通过配置公约的一种代码生成器,它大大简化了Java Bean类型之间的映射的实现,而且类型安全,性能高,使用简单。
3、准备工作
引入依赖,java11环境时
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<mapstruct.version>1.3.1.Final</mapstruct.version>
<maven.compiler.version>3.6.1</maven.compiler.version>
</properties>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
<scope>provided</scope>
</dependency>
4、普通使用方法
4.1 创建DO
package com.eagle.example.mapstruct.dataobject;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
/**
* 订单实体类
*
* @author Bill
* @date Created in 2021/9/23 14:35 <br>
*/
@Data
public class Order implements Serializable {
/**
* 商品标题
*/
private String title;
/**
* 商品价格
*/
private Long price;
/**
* 订单的重量
*/
private Long weight;
/**
* 订单成本
*/
private Long cost;
/**
* 宝贝列表
*/
List<Item> itemList;
/**
* 是否被删除
*/
private String isDeleted;
/**
* 是否完成订单
*/
private Boolean isFinish;
/**
* 创建时间
*/
private String gmtCreat;
/**
* 更新时间
*/
private LocalDateTime gmtModified;
}
package com.eagle.example.mapstruct.dataobject;
import lombok.Data;
/**
* 宝贝信息
*
* @author Bill
* @date Created in 2021/9/26 15:28 <br>
*/
@Data
public class Item {
private String itemName;
private Long price;
}
4.2创建DTO
package com.eagle.example.mapstruct.dto;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author Bill
* @date Created in 2021/9/23 14:40 <br>
*/
@Data
public class OrderDTO implements Serializable {
/**
* 商品标题
*/
private String name;
/**
* 商品价格
*/
private String price;
/**
* 订单的重量
*/
private Long weight;
/**
* 订单成本
*/
private Long cost;
/**
* 宝贝列表
*/
private List<ItemDTO> itemList;
/**
* 是否被删除
*/
private Boolean isDeleted;
/**
* 是否完成订单
*/
private Boolean isFinish;
/**
* 创建时间
*/
private LocalDateTime gmtCreat;
/**
* 更新时间
*/
private LocalDateTime gmtModified;
}
package com.eagle.example.mapstruct.dto;
import lombok.Data;
/**
* 宝贝信息do
*
* @author Bill
* @date Created in 2021/9/26 15:29 <br>
*/
@Data
public class ItemDTO {
private String itemName;
private Long price;
}
package com.eagle.example.mapstruct.dto;
import lombok.Data;
import java.io.Serializable;
/**
* @author Bill
* @date Created in 2021/9/23 14:40 <br>
*/
@Data
public class OrderInfoDTO implements Serializable {
/**
* 商品标题
*/
private String name;
/**
* 商品价格
*/
private String price;
}
4.3创建一个基础的Mapstruct类
可以编写一些基础方法和通用功能,因为随着Localdate的引入DO也都使用了Localdate类型,但是Mapstruct对Localdate支持比较弱,所以引入
package com.eagle.example.mapstruct.convert;
/**
* @author Bill
* @date Created in 2021/9/26 17:39 <br>
*/
import org.mapstruct.Mapper;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Mapper(imports = {LocalDateTime.class, LocalDate.class})
public interface BaseMapstruct {
}
4.4创建映射器接口
当前例子是比较全的一种形态,所以很多不需要使用的自己酌情删除
package com.eagle.example.mapstruct.convert;
/**
* @author Bill
* @date Created in 2021/9/26 17:08 <br>
*/
public class BooleanStrFormat {
private static final String Y_CHAR = "Y";
private static final String N_CHAR = "N";
public String toStr(Boolean isDisable) {
if (isDisable) {
return Y_CHAR;
} else {
return N_CHAR;
}
}
public Boolean toBoolean(String str) {
if (str.equals(Y_CHAR)) {
return true;
} else {
return false;
}
}
}
package com.eagle.example.mapstruct.convert;
import com.eagle.example.mapstruct.dataobject.Order;
import com.eagle.example.mapstruct.dto.OrderDTO;
import com.eagle.example.mapstruct.dto.OrderInfoDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.ap.internal.util.Strings;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* @author Bill
* @date Created in 2021/9/23 16:43 <br>
*/
@Mapper(uses = BooleanStrFormat.class)
public interface OrderMapper extends BaseMapstruct {
OrderMapper INSTANCE = Mappers.getMapper(OrderMapper.class);
String Y_CHAR = "Y";
String N_CHAR = "N";
/**
* 1、属性名不一致转换
* 2、LocalDateTime 转 string
* 3、qualifiedByName 方式Long 转String
* 4、List 对象转换
*
* @param orderDTO
* @return
*/
@Mappings({
@Mapping(source = "name", target = "title"),
@Mapping(target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(target = "price", qualifiedByName = "toLong")
})
Order orderDto2Order(OrderDTO orderDTO);
/**
* Order to orderDto
*
* @param order
* @return
*/
@Mappings({
@Mapping(source = "title", target = "name"),
@Mapping(target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(target = "price", qualifiedByName = "toStr")
})
OrderDTO order2OrderDto(Order order);
default Long toLong(String price) {
if (Strings.isEmpty(price)) {
return 0L;
} else {
return Long.parseLong(price);
}
}
default String toStr(Long price) {
if (price != null) {
return price.toString();
} else {
return "0";
}
}
/**
* List对象转换方式
*
* @param orderDTOList
* @return
*/
List<Order> orderDto2Order(List<OrderDTO> orderDTOList);
/**
* 更新订单信息
*
* @param orderDTO
* @return
*/
@Mappings({
@Mapping(source = "name", target = "title"),
@Mapping(target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(target = "price", qualifiedByName = "toLong")
})
void updateOrder(OrderDTO orderDTO, @MappingTarget Order order);
/**
* 两个对象最终转化成一个对象
*
* @param orderDto
* @param orderInfoDTO
* @return
*/
@Mappings({
@Mapping(source = "orderInfoDTO.name", target = "title"),
@Mapping(source = "orderDto.gmtCreat", target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(source = "orderInfoDTO.price", target = "price", qualifiedByName = "toLong")
})
Order getUserByOrderDTOAndOrderInfoDTO(OrderDTO orderDto, OrderInfoDTO orderInfoDTO);
/**
* 两个对象最终转化成一个对象
*
* @param orderDto
* @param orderInfoDTO
* @return
*/
@Mappings({
@Mapping(source = "orderInfoDTO.name", target = "title"),
@Mapping(source = "orderDto.gmtCreat", target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(target = "price", source = "orderDto.price")
})
Order getUserByOrderDTOAndOrderInfoDTOChoicePrice(OrderDTO orderDto, OrderInfoDTO orderInfoDTO);
}
5、spring依赖注入使用方法
package com.eagle.example.mapstruct.convert;
import org.springframework.stereotype.Component;
/**
* 有需要使用boolean转String方式的转化器
* @author Bill
* @date Created in 2021/9/26 17:08 <br>
*/
@Component
public class SpringBooleanStrFormat {
private static final String Y_CHAR = "Y";
private static final String N_CHAR = "N";
public String toStr(Boolean isDisable) {
if (isDisable) {
return Y_CHAR;
} else {
return N_CHAR;
}
}
public Boolean toBoolean(String str) {
if (str.equals(Y_CHAR)) {
return true;
} else {
return false;
}
}
}
package com.eagle.example.mapstruct.convert;
import com.eagle.example.mapstruct.dataobject.Order;
import com.eagle.example.mapstruct.dto.OrderDTO;
import org.mapstruct.InjectionStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.ap.internal.util.Strings;
import java.util.List;
/**
* @author Bill
* @date Created in 2021/9/23 16:43 <br>
*/
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR, uses = SpringBooleanStrFormat.class)
public interface SpringOrderMapper extends BaseMapstruct {
/**
* orderDto to Order
*
* @param orderDTO
* @return
*/
@Mappings({
@Mapping(source = "name", target = "title"),
@Mapping(target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(target = "price", qualifiedByName = "toLong")
})
Order orderDto2Order(OrderDTO orderDTO);
/**
* Order to orderDto
*
* @param order
* @return
*/
@Mappings({
@Mapping(source = "title", target = "name"),
@Mapping(target = "gmtCreat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(target = "price", qualifiedByName = "toStr")
})
OrderDTO order2OrderDto(Order order);
default Long toLong(String price) {
if (Strings.isEmpty(price)) {
return 0L;
} else {
return Long.parseLong(price);
}
}
default String toStr(Long price) {
if (price != null) {
return price.toString();
} else {
return "0";
}
}
/**
* orderDto to Order by list
*
* @param orderDTOList
* @return
*/
List<Order> orderDto2Order(List<OrderDTO> orderDTOList);
}
6、总结
7、源码和官网
github:eagle-example: 项目例子集合