作者|饭哥
本文简要介绍了 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 依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version></dependency>
- 核心注解@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 依赖
<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version></dependency>
- 对象定义
举例,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 用于实现两个对象之间的属性转换。
三种工具都为开发者提供了较好的便捷性,并一定程度上提升了代码可读性。值得注意的是,很多工具可在编译期自动生成代码,但使用者应该熟悉其基本原理,以避免可能的异常情况,出现问题能及时感知与排查。