本文简要介绍了 GenerateAllSetter / Lombok / Mapstruct 三种法器,各自应用场景稍有不同,供大家参考选择。
背景
日常开发过程中,往往绕不开 DTO / DO / VO 等基础对象定义,或对象属性转换等相关编码。针对对象属性 Getter / Setter 场景,本文简要介绍了 GenerateAllSetter / Lombok / Mapstruct 三种法器,各自应用场景稍有不同,供大家参考选择。
GenerateAllSetter
GenerateAllSetter 是什么?
GenerateAllSetter 是一款 IDEA 插件,可为对象属性批量生成 Setter 代码。
什么时候用?
对象属性赋值,希望自动生成对象所有属性的 Setter 方法。
怎么用?
IDEA 安装插件 GenerateAllSetter
IDEA-Preferences-Plugins,搜索 GenerateAllSetter ,一键安装插件。插件安装后,若未生效请重启 IDEA 。
一键生成对象 Setter 方法
GenerateAllSetter 安装完成后,选中目标对象,单击左边灯泡或使用快捷键 MacOS( Option + 回车)/ Windows( Alt + 回车),即可一键生成对象的 setXxx 方法。
例如,点击 “Generate all setter with default value”,一键生成对象所有 Setter 方法(预设默认值)。
Lombok
Lombok 是什么?
Lombok 是一款 Java 开发插件,可在编译期自动生成对象的基础方法。例如, POJO 类的构造器、 Getter/Setter 、equals 和 toString 等方法,借助 Lombok 只需添加相应注解即可自动生成,无需手动定义。
什么时候用?
对象基础方法定义,希望自动生成 Getter / Setter / toString 等基础方法,让类定义更简洁。
怎么用?
引入 Lombok 依赖
org.projectlombok
lombok
1.16.18
核心注解@Data
作用于类时,为类的所有属性生成 Getter/Setter 方法,并生成类的 toString / equals / canEquals / hashCode 方法。Lombok 自动生成 Getter / Setter 方法时,不会覆盖显式定义的 Getter / Setter 方法。
@Setter:作用于类时,为类的所有属性生成 Setter 方法;作用于类的某个属性时,为该属性生成 Setter 方法。
@Getter:作用于类时,为类的所有属性生成 Getter 方法;作用于类的某个属性时,为该属性生成 Getter 方法。
@AllArgsConstructor:作用于类时,生成该类的全参构造函数。
@NoArgsConstructor:作用于类时,生成该类的无参构造函数。
@ToString:作用于类时,生成对应的 toString 方法。
@EqualsAndHashCode:作用于类时,重写 equals 和 hashcode 方法。
@Builder:通过 Builder 链式创建新对象,不需要逐行添加 Setter 方法,可简化代码行数,提升编码体验。
@NotNull:作用于方法入参,如果对应入参传了 null 值,将抛出空指针异常。
@Synchronized:作用于方法,可锁定指定对象;如果不指定,则默认创建一个对象锁定。
@Accessors(chain = true):使用链式设置属性,Setter 方法返回 this 对象。
@RequiredArgsConstructor:在类上添加 @RequiredArgsConstructor(staticName = "of") 时,生成一个静态方法。
@FieldDefaults:设置属性使用范围,如 private / public 等,也可以设置属性是否被 final 修饰。
@Cleanup: 关闭流对象、连接点等。
示例 1 :
@Data 注解,自动生成对象的 Getter、Setter、equals、hashcode 和 toString 方法。
编译后生成代码如下:
示例 2 :
@Getter / @Setter / @AllArgsConstructor / @NoArgsConstructor / @Builder 注解,分别生成对象的 Getter、Setter、全参构造器、无参构造器和 Builder 方法。
编译后生成代码如下:
更多功能,可参考官网:https://projectlombok.org
Mapstruct
Mapstruct 是什么?
Mapstruct 是一款 Java 属性映射工具,可实现源对象和目标对象之间的属性映射。通过 Mapstruct 定义 Mapper 接口,Mapstruct 将在编译期生成该 Mapper 接口的实现类,该实现类内部封装了对象属性转换逻辑。值得注意的是,Spring 和 Apache 提供了 BeanUtils 工具,同样可实现对象属性转换,但由于底层基于反射实现,需在运行期进行属性转换,效率相对较低,因此应尽量避免使用 BeanUtils 工具。
什么时候用?
对象间属性映射,希望自动映射关联属性,或自定义不同属性的映射关系。
怎么用?
引入 Mapstruct 依赖
org.mapstruct
mapstruct
${org.mapstruct.version}
对象定义
举例,PosOrder 对象转换为 WorkOrder 对象,两个对象属性不完全对等。
PosOrder(订单):
@Data
public class PosOrder implements Serializable {
private static final long serialVersionUID = 690962385718485646L;
/**
* 记录生成时间
*/
private Date gmtCreate;
/**
* 订单ID
*/
private Long id;
/**
* 买家信息
*/
private User user;
// ...
}
WorkOrder(工单):
@Data
public class WorkOrder implements Serializable {
private static final long serialVersionUID = 890962385718485649L;
/**
* 记录生成时间
*/
private Date gmtCreate;
/**
* 工单ID
*/
private String workNo;
/**
* 买家信息
*/
private Customer customer;
/**
* 删除标记
*/
private String isDeleted;
/**
* 记录序列ID
*/
private String sequenceId;
}
WorkOrderMapper 转换器定义
定义接口 WorkOrderMapper,并添加类注解 @Mapper ,MapStruct 会自动生成对应实现类。
@Mapper 的 componentModel 属性,用于指定实现类的类型。
default:通过 Mappers.getMapper(Class) 方式获取实例对象;
spring:接口实现类自动添加注解 @Component,可通过 @Autowired 方式注入。
@Mapping:属性映射,若源对象属性与目标对象属性名字一致,将自动映射同名属性;此外,支持自定义属性映射关系。
source:源属性
target:目标属性
dateFormat:String 和 Date 日期相互转换
ignore: 忽略这个字段
WorkOrderMapper 代码如下:
@Mapper
public interface WorkOrderMapper {
/**
* Mapper实例
*/
WorkOrderMapper INSTANCE = Mappers.getMapper(WorkOrderMapper.class);
/**
* PosOrder 转换为 WorkOrder
* 说明:
* - PosOrder.id 映射为 WorkOrder.workNo
* - PosOrder.user.userName 映射为 WorkOrder.customer.name
* - WorkOrder.isDeleted 默认为常量"n"
* - WorkOrder.sequenceId 通过 getSequenceId() 方法动态生成
*/
@Mapping(target = "workNo", source = "id" )
@Mapping(target = "customer.name", source = "user.userName")
@Mapping(target = "isDeleted", constant = "n")
@Mapping(target = "sequenceId", expression="java(getSequenceId())")
WorkOrder transFrom(PosOrder posOrder);
/**
* WorkOrder 转换为 PosOrder
*/
@InheritInverseConfiguration(name="transTo")
PosOrder transTo(WorkOrder workOrder);
/**
* 映射器可添加自定义方法,如获取序列ID
*/
default String getSequenceId(){
String uuid = UUID.randomUUID().toString();
return uuid;
}
}
编译后生成的 Mapper 实现类如下:
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2021-09-27T21:55:09+0800",
comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_151 (Oracle Corporation)"
)
public class WorkOrderMapperImpl implements WorkOrderMapper {
@Override
public WorkOrder transFrom(PosOrder posOrder) {
if (posOrder == null) {
return null;
}
WorkOrder workOrder = new WorkOrder();
workOrder.setCustomer(userToCustomer(posOrder.getUser()));
if (posOrder.getId() != null) {
// Mapping 映射属性生成
workOrder.setWorkNo(String.valueOf(posOrder.getId()));
}
// 同名属性自动映射
workOrder.setGmtCreate(posOrder.getGmtCreate());
// 设置默认值
workOrder.setIsDeleted("n");
// 自定义方法
workOrder.setSequenceId(getSequenceId());
return workOrder;
}
@Override
public PosOrder transTo(WorkOrder workOrder) {
if (workOrder == null) {
return null;
}
PosOrder posOrder = new PosOrder();
posOrder.setUser(customerToUser(workOrder.getCustomer()));
if (workOrder.getWorkNo() != null) {
posOrder.setId(Long.parseLong(workOrder.getWorkNo()));
}
posOrder.setGmtCreate(workOrder.getGmtCreate());
return posOrder;
}
/**
* 内部对象映射
*/
protected Customer userToCustomer(User user) {
if ( user == null ) {
return null;
}
Customer customer = new Customer();
customer.setName(user.getUserName());
// 同名属性自动映射
customer.setId(user.getId());
return customer;
}
/**
* 内部对象映射
*/
protected User customerToUser(Customer customer) {
if ( customer == null ) {
return null;
}
User user = new User();
user.setUserName( customer.getName() );
user.setId( customer.getId() );
return user;
}
对于更复杂的属性转换,Mapstruct 还支持 List / Map / 枚举映射 / 时间 等类型转换。需要提醒的是,编译后最好检查 Mapstruct 自动生成的代码,以避免可能的异常情况,如类型自动转换可能丢失精度等。
借助 WorkOrderMapper 转换器实现对象转换
定义 WorkOrderMapper 后,即可直接使用 Mapper 实例将 PosOrder 转换为 WorkOrder 对象。
WorkOrder workOrder = WorkOrderMapper.INSTANCE.transfer(posOrder);
在上面例子中, 我们通过 Mappers.getMapper(xxx.class) 方式获取对应 Mapper 。接口本身定义了 INSTANCE 字段,用于获取 Mapper 实例。此外,Mapstruct 支持 Spring 依赖注入方式,通过 @Mapper(componentModel = “spring”) 即可将当前 Mapper 注册进 Spring 容器,后续在其它类直接注入即可。
总结
本文简要介绍了三种对象属性编码提效工具。
GenerateAllSetter 用于自动生成对象属性的 Setter 方法;
Lombok 用于自动生成类的 Getter / Setter / toString 等基础方法;
Mapstruct 用于实现两个对象之间的属性转换。
三种工具都为开发者提供了较好的便捷性,并一定程度上提升了代码可读性。值得注意的是,很多工具可在编译期自动生成代码,但使用者应该熟悉其基本原理,以避免可能的异常情况,出现问题能及时感知与排查。
✿ 拓展阅读
作者|饭哥
编辑|橙子君
出品|阿里巴巴新零售淘系技术