先简单比较一下BeanUtil和MapStruct的转换效率
分别转换了2w条数据花费时间对比。单位毫秒,其实也没多大差别。性能瓶颈一般不在这上面。
BeanUtil源码
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
@Nullable String... ignoreProperties) throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
// 拿到目标对象属性信息
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
// 拿到忽略拷贝信息。
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
for (PropertyDescriptor targetPd : targetPds) {
// 拿到目标对象写(set)方法
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
// 拿到源对象 targetPd.getName() 的属性信息 (可以看出只支持名字一样才能copy)
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
// 源对象sourcePd这个属性的读方法
Method readMethod = sourcePd.getReadMethod();
// 判断是否有set方法和get方法
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
// 判断get方法是否公共方法
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
// 如果是私有方法,设置可访问
readMethod.setAccessible(true);
}
// 调用get方法
Object value = readMethod.invoke(source);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
// 调用set方法
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
缺点:
- 浅拷贝。
- 源对象和目标对象名字一致才能copy。
- 反射效率低下。
MapStruct 使用
/**
* @author Administrator
* @description TODO
* @date 2022/5/31 16:05
*
* @Mapper 只有在接口加上这个注解,MapStruct 才会去实现该接口,@Mapper 里有个 componentModel 属性,主要是指定实现类的类型,一般用到两个:
* default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象。
* spring:在接口的实现类上自动添加注解 @Component,可通过 @Autowired 方式注入。
* @Mappings:配置多个@Mapping
* @Mapping:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性
* source:源属性
* target:目标属性
* dateFormat:String 到 Date 日期之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat的日期格式
* expression:使用Java方法来格式化值
* ignore: 忽略这个字段
*/
@Mapper(mappingControl= DeepClone.class) // 支持深拷贝
public interface OrderConvert {
OrderConvert INSTANCE = Mappers.getMapper(OrderConvert.class);
// @Mappings({
// @Mapping(source = "showCoursePrice", target = "showCoursePrice"),
// @Mapping(target = "status", expression = "java(user.getUserStatusEnum().getCode())"),
// @Mapping(source = "createTime", target = "createTimeFormat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
// @Mapping(source = "userInfo.address", target = "address"),
// @Mapping(target = "password", ignore = true)
// })
OrderDto entity2dto(Order order);
Order dto2entity(OrderDto orderDto);
List<OrderDto> EntityList2AddOrderVo(List<Order> orders);
}
调用方式:
Order order = OrderConvert.INSTANCE.entity2dto(orderDto)