想必大家一定见过这样的代码:
customerDO.setId( customerDTO.getId() );
customerDO.setName( customerDTO.getName() );
customerDO.setPhone( customerDTO.getPhone() );
customerDO.setShopCode( customerDTO.getShopCode() );
customerDO.setReference( customerDTO.getReference() );
customerDO.setDuty( customerDTO.getDuty() );
customerDO.setIsDefeat( customerDTO.getIsDefeat() );
···
这样的:
if(null == id) {
throw ParamMissingException("id不能为空");
}
if(null == name) {
throw ParamMissingException("名称不能为空");
}
if(null == phone) {
throw ParamMissingException("手机号不能为空");
}
...
还有这样的:
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Long getId() {
return this.id;
}
public String getName() {
return this.name;
}
public String getPhone() {
return this.phone;
}
...
尤其是前两种代码,可能与我们的业务逻辑并无相关,却占据了很多地方。我称这种代码为项目的“脂肪块"。当这种代码大量充斥我们的项目时,我们的项目会看起来很臃肿。当你接手一个项目时,如果存在大量这种代码,想要理清业务逻辑,必须穿过它们的包围。这使我们很痛苦。那么有没有办法简化这种代码呢?接下来,我就为大家介绍几个简单好用的工具。
mapstruct是一个用于自定义bean映射的框架。简单的讲,就是用于数据类型转换,解决上面演示的第一种“脂肪块"。这篇文章以应用为主,不涉及过多理论解释。直接上使用步骤:
1、引入依赖
org.mapstruct
mapstruct-jdk8
1.2.0.Final
org.mapstruct
mapstruct-processor
1.2.0.Final
2、定义一个interface,接口名自定义。加上@Mapper注解。在其中定义转换数据类型的方法。入参为原数据类型,返回值目标数据类型。方法名自定义。
@Mapper
public interface CustomerMapper {
CustomerMapper INSTANCE = Mappers.getMapper(CustomerMapper.class);
CustomerDO toDO(CustomerDTO customerDTO);
List<CustomerDO> toDOs(List<CustomerDTO> carDTOList);
CustomerDTO toDTO(CustomerDO customerDO);
List<CustomerDTO> toDTOs(List<CustomerDO> customerDO);
}
3、编译项目。
4、调用方法。
CustomerDTO customerDTO = CustomerMapper.INSTANCE.toDTO(customerDO);
原本需要写的多行代码(开头演示的第一种代码),被这一行所代替。除了大大简化代码之外,使用这个框架还有一个很大的好处就是:当你需要调整字段时,只需在DO和DTO里调整就好,不必再去业务方法里增加或减少set··(get··)这种代码,降低了维护的难度。
上面的转换需要字段类型和字段名称一致。但有时我们需要把枚举类型转换为数字或字符串。
1、枚举转数字
方法一:
在枚举类加上code字段
public enum NumberEnum {
ONE(1),
TWO(2);
private Integer code;
NumberEnum(Integer code) {
this.code = code;
}
}
然后在Mapper接口中加上这样的注解:
@Mapping(source = "numberEnum.code", target = "number")
UserDO toDO(UserDTO userDTO);
方法二:
在Mapper中加转换方法:
default Integer getNumberOrdinal(NumberEnum numberEnum) {
if (NumberEnums == null) {
return null;
}
return numberEnums.ordinal();
}
这两种方法的区别:
方法一可以自定义要转换的数字,而方法二只能转换为枚举值的序数。
2、枚举转字符串
情况一:转为枚举值的name
框架支持直接转换,不需要特殊处理
情况二:转为汉字文本
同转数字的方法一,在枚举类中定义desc字段:
public enum NumberEnum {
ONE(1, 壹),
TWO(2,贰);
private Integer code;
private String desc;
NumberEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
}
在Mapper中加注解:
@Mapping(source = "numberEnum.desc", target = "number")
UserDO toDO(UserDTO userDTO);
在使用default关键字在接口中增加转换方法的时候,框架会根据方法的入参和返回值来寻找要转换的字段。这样有时会在不需要使用方法转换的字段上也使用这个方法,要小心避免。
lombok与mapstruct原理相同,也是在编译的时候帮助用户生成代码。lombok主要用在数据类型上,简化一些重复的代码。
比如:
@Data
public class TaskDTO implements Serializable {
private static final long serialVersionUID = 8460254643609851598L;
private String uuid;
private String userId;
private TasksEnums tasksEnums;
}
@Date注解会帮我们生成get、set、equals、hashCode、toString等方法。
虽然这些方法可以通过IDE生成,但是在改动字段时,还需要重新生成。使用@Date注解,更容易扩展。
我们在写方法时,首先要对传入的参数进行校验。这些代码会占用大量的篇幅。validation是把参数检验的工作提到了框架层面。
首先,我们需要引两个包:
<validation.api.version>1.0.0.GAvalidation.api.version>
<hibernate.validator.version>4.2.0.Finalhibernate.validator.version>
<dependency>
<groupId>javax.validationgroupId>
<artifactId>validation-apiartifactId>
<version>${validation.api.version}version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-validatorartifactId>
<version>${hibernate.validator.version}version>
dependency>
然后在需要校验的参数上加上注解:
@NotNull(message = "买家手机号不能为空")
private String phone;
除了@NotNull注解,validation还提供了正则、日期、数字区间等注解,可以满足开发中的各种情况。
最后在发布dubbo服务配置文件里加上:
<dubbo:service interface="com.xx.xx.xx.xx.XxService" ref="XxService" validation="true"/>
我们开发中经常会遇到重复性的代码,这时我们要想着能否在框架层面解决这个问题。不仅仅是为了少写代码,还能使我们的代码更好阅读,更好维护,更好扩展。