cover_image

推荐GET/SET 提效工具

饭哥 大淘宝技术
2021年10月12日 08:20

图片

本文简要介绍了 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(订单):


@Datapublic class PosOrder implements Serializable {    private static final long serialVersionUID = 690962385718485646L;    /**     * 记录生成时间     */    private Date gmtCreate;    /**     * 订单ID     */    private Long id;    /**     * 买家信息     */    private User user;        // ...}


WorkOrder(工单):


@Datapublic 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 属性,用于指定实现类的类型。

  1. default:通过 Mappers.getMapper(Class) 方式获取实例对象;

  2. spring:接口实现类自动添加注解 @Component,可通过 @Autowired 方式注入。

@Mapping:属性映射,若源对象属性与目标对象属性名字一致,将自动映射同名属性;此外,支持自定义属性映射关系。

  1. source:源属性

  2. target:目标属性

  3. dateFormat:String 和 Date 日期相互转换

  4. ignore: 忽略这个字段

WorkOrderMapper 代码如下:


@Mapperpublic 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 容器,后续在其它类直接注入即可。


总结


本文简要介绍了三种对象属性编码提效工具。


  1. GenerateAllSetter 用于自动生成对象属性的 Setter 方法;

  2. Lombok 用于自动生成类的 Getter / Setter / toString 等基础方法;

  3. Mapstruct 用于实现两个对象之间的属性转换。


三种工具都为开发者提供了较好的便捷性,并一定程度上提升了代码可读性。值得注意的是,很多工具可在编译期自动生成代码,但使用者应该熟悉其基本原理,以避免可能的异常情况,出现问题能及时感知与排查。


✿  拓展阅读
 

图片

图片

作者|饭哥
编辑|橙子君
出品|阿里巴巴新零售淘系技术
图片
图片
继续滑动看下一个
大淘宝技术
向上滑动看下一个