目录
前言
在我们Java开发的过程中不可避免的会遇到需要将一个类转换为另一个类的情况,比如我们从数据中或者别人的接口中查询出来的类转换为我们对外展示所用的视图类。可能有人会说,那直接用返回的类当做视图类不就可以了,还省了转换的步骤和时间。但是如果当返回内容增加或修改时就很容易污染我们的对外视图类,所以类转换也变得不可或缺。下面将展示四种种类转换方式。
类型转换方式一:
JSON
CategoryVo categoryVo = JSONObject.parseObject(JSONObject.toJSONString(productCategory), CategoryVo.class);
类型转换方式二:
get/set
ProductCategory productCategory=new ProductCategory();
productCategory.setCategoryCode("773");
productCategory.setCategoryName("张三");
CategoryVo categoryVo=new CategoryVo();
categoryVo.setCode(productCategory.getCategoryCode());
categoryVo.setName(productCategory.getCategoryName());
类型转换方式三:
BeanUtils
ProductCategory productCategory=new ProductCategory();
productCategory.setCategoryCode("773");
productCategory.setCategoryName("张三");
CategoryVo categoryVo=new CategoryVo();
try {
BeanUtils.copyProperties(productCategory,categoryVo);
} catch (Exception e) {
e.printStackTrace();
}
类型转换方式四:
类型转换方式4就是我们的MapStruct下面会详细讲解,先给大家展示一下这四种转换方式的性能比较,如下:
从表中可以看到JSON和BeanUtils其实是相对较慢的转换方式,但是其实在编码中还是有使用这种方式进行转换的,究其原因可能就是这种方式是代码量最小的。
get/set是效率最高的,毕竟不管使用那种方式,最底层本质其实也类似与get/set,最直接也就最简单最快捷,但是这种方式有一个致命问题就是不太易于维护,尤其是存在许多字段名一样的情况下就更觉得JSON更好用了。
千呼万唤始出来-mapstruct,即高效又便捷,下面就为大家揭开mapstruct的神秘面纱。
一、MapStruct的使用
1.1依赖包
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.2.0.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.2.0.Final</version>
</dependency>
- org.mapstruct:mapstruct:包含了一些必要的注解,例如@Mapping。r若我们使用的JDK版本高于1.8,当我们在pom里面导入依赖时候,建议使用坐标是:org.mapstruct:mapstruct-jdk8,这可以帮助我们利用一些Java8的新特性。
- org.mapstruct:mapstruct-processor:注解处理器,根据注解自动生成mapper的实现。
1.2使用
1.2.1定义实体类及被映射类
/**
*实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CategoryVo {
private String code;
private String name;
private Integer categoryId;
private String time;
private Integer age;
private String remark;
}
/**
*被映射类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductCategory {
private String categoryCode;
private String categoryName;
private Integer categoryId;
private Date time;
private String age;
private String remark;
}
1.2.1定义接口
/**
* 共用公共转换接口,可省略,方法在实现类中定义实现即可
* @param <S> 实体类
* @param <T> 被映射类
*/
public interface BasicObjectConvert<S,T> {
T toTarget(S s);
@InheritConfiguration(name="toTarget")
List<T> toTargetList(List<S> sList);
S toSource(T t);
@InheritConfiguration(name="toSource")
List<S> toSourceList(List<T> tlist);
}
/**
* 转换mapper类
*/
@Mapper(unmappedTargetPolicy = ReportingPolicy.WARN)
public interface TestConvert extends BasicObjectConvert<ProductCategory, CategoryVo> {
TestConvert testConvert= Mappers.getMapper(TestConvert.class);
@Override
@Mappings({
@Mapping(source = "categoryCode", target = "code"), //配置字段名不一致时的映射
@Mapping(source = "categoryName", target = "name"), //配置字段名不一致时的映射
@Mapping(source = "age", target = "age"), //配置类型不一致时的映射
@Mapping(source = "time", target = "time",dateFormat = "yyyy-MM-dd HH:mm:ss"), //配置日期类型格式化的映射
@Mapping(target = "remark", ignore = true) //配置忽略某字段的映射
})
CategoryVo toTarget(ProductCategory s);
}
- @InheritConfiguration 用于继承刚才toTarget方法的配置
- @Mapper 只有在接口加上这个注解, MapStruct 才会去实现该接口
componentModel:
default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象
spring:在接口的实现类上自动添加注解 @Component,可通过 @Autowired 方式注入
unmappedTargetPolicy: 在映射方法的目标对象的属性未填充源值的情况下应用的默认报告策
ERROR : 任何未映射的目标属性都将导致映射代码生成失败
WARN : 任何未映射的目标属性将在构建时引发警告
IGNORE : 未映射的目标属性被忽略
- @Mapping:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性
source:源属性
target:目标属性
dateFormat:String 到 Date 日期之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat的日期格式
ignore: 忽略这个字段
- @Mappings:配置多个@Mapping
二.MapStruct原理
在 target/generated-sources/annotations 里可以看到,代码中可以看到其生成了一个实现类, 而代码也类似于我们手写。
这个在编译期生成的代码,性能上是比反射要快不少的。
MapStruct是利用编译期动态生成set/get代码的class文件 ,在运行时直接调用该class文件。 该方式实际上仍会存在set/get代码,只是不需要自己手写。
三、MapStruct使用注意事项
1.当mapStruct与lombok一起使用时要注意lombok的版本,过高时会出现不兼容的问题,报错如下: