Foreword
statement:
1, DO (business entity objects), DTO (data transfer object).
In a mature project, especially now distributed system, between the application and after application, as well as a separate application segmentation module, DO generally will not let external dependencies, this time the need to provide external interface module in put DTO object for transmission, that is, DO internal objects, objects outside DTO, DTO may be changed according to business needs and do not need to map all the attributes of DO.
- In our external exposure Dubbo interface, usually so defined interface class
/**
* 获取营销信息
*
* @param hallId 场馆编号
* @param modelCode 车型代码
* @return
*/
BrandMarketInfoDTO getBrandMarketInfo(String hallId, String modelCode);
复制代码
- In the interface mapping and data persistence layer implementation in general, so we will write
/**
* 获取水牌营销信息
*
* @param hallId
* @param modelCode
* @return
*/
HallCarManageDO getBoardMarketInfo(@Param("hallId") String hallId, @Param("modelCode") String modelCode);
复制代码
But we HallCarManageDO and BrandMarketInfoDTO attributes and attribute type is not equal. This mutual conversion between the object and the subject, we need to have a tool specifically designed to solve the conversion problem, after all, each field get / set will be very troublesome.
Common tools and implementation
Here are the common property of the majority of development engineers Bean Replication Tool
- Spring.BeanUtils
- Cglib.BeanCopier
- MapStruct
The following selection attribute evaluation function for each tool to compare different classes
BeanUtils
import org.springframework.beans.BeanUtils;
/**
* @author james mu
* @date 2019/10/22
*/
public class PojoUtils {
/**
*
* @param source 源对象
* @param clazz 目标对象
*
* @return 复制属性后的对象
*/
public static <T> T copyProperties(Object source, Class<T> clazz) {
if (source == null) {
return null;
}
T target;
try {
target = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("通过反射创建对象失败");
}
BeanUtils.copyProperties(source, target);
return target;
}
}
复制代码
springframework
The BeanUtils also by java 内省机制
get getter / setter, and then copy the properties in order to achieve invoked by reflection.
BeanCopier
Dependent reference
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
复制代码
Tools to achieve
import net.sf.cglib.beans.BeanCopier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author james mu
* @date 2019/10/22
*/
public class BeanCopierUtils {
/**
* BeanCopier拷贝速度快,性能瓶颈出现在创建BeanCopier实例的过程中。
* 所以,把创建过的BeanCopier实例放到缓存中,下次可以直接获取,提升性能
*/
public static Map<String, BeanCopier> beanCopierMap = new ConcurrentHashMap<String, BeanCopier>();
/**
* cp 对象赋值
*
* @param source 源对象
* @param target 目标对象
*/
public static void copyProperties(Object source, Object target) {
String beanKey = generateKey(source.getClass(), target.getClass());
BeanCopier copier = null;
if (!beanCopierMap.containsKey(beanKey)) {
copier = BeanCopier.create(source.getClass(), target.getClass(), false);
beanCopierMap.put(beanKey, copier);
} else {
copier = beanCopierMap.get(beanKey);
}
copier.copy(source, target, null);
}
private static String generateKey(Class<?> class1, Class<?> class2) {
return class1.toString() + class2.toString();
}
}
复制代码
-
Dynamic proxies, to generate the class bytecode, and then through the Java reflection to Class, call its copy method.
-
We can see here uses ConcurrentHashMap access
copier
, because BeanCopier.create use the cache, the process also consumes resources, the proposed global initialization only once. -
Support for custom converter.
MapStruct
MapSturct
Generating a type-safe, high-performance and no code dependent JavaBean mapping annotation processor (annotation processor).
Grasp the main points:
- Annotation Processor
- May be generated
JavaBean
that the mapping between the code - Type safety, high performance, non-dependent
From the literal understanding, we can know, this tool can help us achieve JavaBean
conversion between, by way of comment.
Meanwhile, as a tool class, compared to handwriting, which should have a convenient, error-prone characteristics.
getting Started
Dependent reference
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.0.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.0.Final</version>
<scope>provided</scope>
</dependency>
复制代码
Case presentation
UserDO
import lombok.Data;
import lombok.ToString;
import java.util.Date;
/**
* @author james mu
* @date 2019/10/22
*/
@Data
@ToString
public class UserDO {
private String name;
private String password;
private Integer age;
private Date birthday;
private String sex;
}
复制代码
UserDTO
import lombok.Data;
import lombok.ToString;
/**
* @author james mu
* @date 2019/10/22
*/
@Data
@ToString
public class UserDTO {
private String name;
private String age;
private String birthday;
private String gender;
}
复制代码
UserConvertUtils
import com.sanshengshui.javabeanconvert.DO.UserDO;
import com.sanshengshui.javabeanconvert.DTO.UserDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author james mu
* @date 2019/10/22
*/
@Mapper
public interface UserConvertUtils {
UserConvertUtils INSTANCE = Mappers.getMapper(UserConvertUtils.class);
/**
* 类型转换
*
* @param userDO UserDO数据持久层类
* @return 数据传输类
*/
@Mappings({
@Mapping(target = "gender", source = "sex")
})
UserDTO doToDTO(UserDO userDO);
}
复制代码
- DTO and DO in the same attribute name when the default mapping (such as name), different types of properties will be the same attribute name mapping (such as birthday, a Data, a String)
- DTO and attribute names DO different, need
@Mapping
to form a clear relationship mapping (e.g., corresponding to Gender sex) - No mapping for the attribute is ignored (as UserEntity the password)
The results are as follows:
UserDO(name=snow, password=123, age=20, birthday=Tue Oct 22 17:10:19 CST 2019, sex=男)
+-+-+-+-+-+-+-+-+-+-+-
UserDTO(name=snow, age=20, birthday=19-10-22 下午5:10, gender=男)
复制代码
MapStruct analysis
Above, I wrote three steps to achieve from UserDTO
the UserDO
conversion.
Then, as an annotation processor, through MapStruct
the generated code has the advantages of how to do?
high performance
Java reflection principle and low reflectivity reasons: juejin.im/post/5da33b...
This is reflected relatively speaking, the need to read the contents of the reflection bytecode, spending will be relatively large. Through MapStruct
to the generated code, which is similar to the human hand. It can be guaranteed speed.
The previous example can be seen in the generated code after compilation. You can see the target / generated-sources / annotations years.
Corresponding code
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2019-10-22T17:10:17+0800",
comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_222 (Azul Systems, Inc.)"
)
public class UserConvertUtilsImpl implements UserConvertUtils {
@Override
public UserDTO doToDTO(UserDO userDO) {
if ( userDO == null ) {
return null;
}
UserDTO userDTO = new UserDTO();
userDTO.setGender( userDO.getSex() );
userDTO.setName( userDO.getName() );
if ( userDO.getAge() != null ) {
userDTO.setAge( String.valueOf( userDO.getAge() ) );
}
if ( userDO.getBirthday() != null ) {
userDTO.setBirthday( new SimpleDateFormat().format( userDO.getBirthday() ) );
}
return userDTO;
}
}
复制代码
You can see that generates an implementation class, and the code is also similar to our hand-written, easy to understand.
Performance Comparison
Time-consuming, the number of executions test between two simple conversion Bean were 10,100,1k, 10k, 100k, time unit is ms.
to sum up
Although the low reflection efficiency, but this time is very small. The performance and function of the dimensions of different tools, personal recommendations converting operation or when the object is small when the high performance requirements of the application, try not to use the tool, but the handwriting getter / setter; without considering performance, Common Object Conversion You can use Cglib.BeanCopier, converting complex objects using MapStruct.