目录
前言
系统变的复杂,系统的层次划分越来越细,边界也越来越明确。 然后每一层之间一般都有自己要处理的领域对象,统称为pojo一般在model或者domain包下(类的后缀不能为pojo)。
常见的一些模型类型:
- PO、DO:持久层对象,一般和数据库直接打交道。
- DTO:数据传输对象,系统之间的交互,再服务层提供服务的时候输出到其它系统。
- VO:视图对象,用于前端模型展示。 当然有时候前端也可以看做另外一个系统,使用DTO模型;
- BO:业务逻辑对象,比较少用...
为什么模型要分这么多层?
在复杂一点的业务中,业务建模是非常有必要的,一定要抽象出业务上常用的领域模型,统一技术和非技术同学的语言。
建完模型之后,在技术的系统中,为了方便维护代码,分离关注点,也会进行再次分层,让每一层解决特定的问题。模型的分层是随着系统的分层而来的;试想所有的模型属性在一个对象中,这个对象你看的懂吗?
举个实际的案例:
- 数据层一般用DO
- 现在要透出数据给其他系统,DO中一般都会有创建人,创建时间,修改人,修改时间,当前对象所处的环境等信息; 但是外部的系统需要环境、创建人信息吗? 很多时候不需要,站在数据安全的角度,一般只透出必要的字段就可以; 这些要输出要外部系统的必要字段,一般定义在DTO中。
- 到前端系统,前端系统展示上所需的逻辑和输出到外部系统的又有点不太一样,前端系统可能要创建人,创建时间,但是不要另外一些东西,或者一些敏感的配置不能透出给前端,这个时候一般也会再定义一个新的对象。
简单说就是当我们的系统要输出能力到外部系统的时候,不同系统要的数据不一样,数据安全要求我们不能透出这么多的数据,一定要做一层处理。 另外给另外一个系统关注的数据,而不是一股脑的全部都给对方,对方处理起来也方便。
模型之间的转换
建议不要用的方式
- 手写get\\set; 虽然性能高,但是费劲并且眼花缭乱,一不小心就写错了,难以维护,复用度不高
- BeanUtils,apacha和spring包下都有对应的类,但是底层用到的都是反射,性能比较差,大流量的情况下一般不用
- 直接fastjson,gc会很频繁,而且性能比较差
常用的方式
- cglib的beanCopier,开销在创建BeanCopier,一般在创建类的时候提前创建好一个,在代码运行的时候直接进行copy,性能接近原生。
- mapstruct 性能和原生代码一样,支持复杂的转化场景,实现原理同lombok编译的时候生成对应的代码。
以上从技术分类的角度来看:
- 反射:fastjson,beanutil 都不建议用
- get\\set: beancoper通过字节码进行getset,mapstruct编译的时候生成getset。 性能相对较好。
使用方式
个人觉得,如果说对象比较简单的时候,使用BeanCopier就可以了,因为spring的aop依赖cglib,默认情况下就已经引入了对应的包了,不需要额外的依赖直接就可以用。
如果很复杂的模型之间的转换,并且对性能有更极致的要求,考虑使用下MapStruct。
定义对象
UserDO
@Data
public class UserDO {
private Long id;
private String name;
private Integer gender;
private String password;
private Date gmtCreate;
private Date gmtModified;
}
UserDTO
@Data
public class UserDTO {
private Long id;
private String name;
private Integer gender;
}
BeanCopier
最简单的使用方式
BeanCopier beanCopier = BeanCopier.create(UserDO.class, UserDTO.class, false); bean.copy即可;
private static void simpleBeanCopy() {
BeanCopier beanCopier = BeanCopier.create(UserDO.class, UserDTO.class, false);
UserDO userDO = new UserDO();
userDO.setId(1L);
userDO.setName(\"aihe\");
userDO.setGmtCreate(new Date());
userDO.setGender(0);
userDO.setPassword(\"xxxxxx\");
UserDTO userDTO = new UserDTO();
beanCopier.copy(userDO, userDTO,null);
Assert.assertEquals(\"名称未成功拷贝\",userDTO.getName(),\"aihe\");
Assert.assertEquals(\"Id未成功拷贝\", 1L, (long)userDTO.getId());
Assert.assertEquals(\"性别未成功拷贝\", Integer.valueOf(0),userDTO.getGender());
}
创建可复用的BeanCopier工具类
package me.aihe.daka;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.sf.cglib.beans.BeanCopier;
/**
* @author : aihe
* @date : 2022/9/12 9:21 AM
* 使用场景:
* 功能描述:
*/
public class BeanCopyUtils {
/**
* beanCopier缓存
* 由sourceClass和targetClass可以确定一个唯一的BeanCoper,因此使用二级Map;
*/
private static Map<Class<?>, Map<Class<?>, BeanCopier>> beanCopierMap = new ConcurrentHashMap<>();
/**
* 直接指定Bean对象进行拷贝
* @param sourceBean
* @param targetBean
* @param <S>
* @param <T>
*/
public static <S,T> void copy(S sourceBean,T targetBean){
@SuppressWarnings(\"unchecked\")
Class<S> sourceClass = (Class<S>) sourceBean.getClass();
@SuppressWarnings(\"unchecked\")
Class<T> targetClass = (Class<T>) targetBean.getClass();
BeanCopier beanCopier = getBeanCopier(sourceClass,targetClass);
beanCopier.copy(sourceBean,targetBean,null);
}
/**
* 转换方法
* @param sourceBean 原对象
* @param targetClass 目标类
* @param <S>
* @param <T>
* @return
*/
public static <S,T> T convert(S sourceBean,Class<T> targetClass){
try {
assert sourceBean!=null;
T targetBean = targetClass.newInstance();
copy(sourceBean,targetBean);
return targetBean;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static <S,T> BeanCopier getBeanCopier(Class<S> sourceClass, Class<T> targetClass ){
Map<Class<?>,BeanCopier> map = beanCopierMap.get(sourceClass);
if(map == null || map.isEmpty()){
BeanCopier newBeanCopier = BeanCopier.create(sourceClass, targetClass, false);
Map<Class<?>,BeanCopier> newMap = new ConcurrentHashMap<>();
newMap.put(targetClass,newBeanCopier);
beanCopierMap.put(sourceClass,newMap);
return newBeanCopier;
}
BeanCopier beanCopier = map.get(targetClass);
if(beanCopier == null){
BeanCopier newBeanCopier = BeanCopier.create(sourceClass, targetClass, false);
map.put(targetClass,newBeanCopier);
return newBeanCopier;
}
return beanCopier;
}
}
同上:
UserDO userDO = new UserDO();
userDO.setId(1L);
userDO.setName(\"aihe\");
userDO.setGmtCreate(new Date());
userDO.setGender(0);
userDO.setPassword(\"xxxxxx\");
UserDTO userDTO = new UserDTO();
BeanCopyUtils.copy(userDO, userDTO);
Assert.assertEquals(\"名称未成功拷贝\",userDTO.getName(),\"aihe\");
Assert.assertEquals(\"Id未成功拷贝\", 1L, (long)userDTO.getId());
Assert.assertEquals(\"性别未成功拷贝\", Integer.valueOf(0),userDTO.getGender());
MapStruct
引入mapstruct
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.mapstruct.version>1.5.2.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.20</org.projectlombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
<!-- IntelliJ does not pick up the processor if it is not in the dependencies.
There is already an open issue for IntelliJ see https://youtrack.jetbrains.com/issue/IDEA-150621
-->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
简单Demo
定义Mapper
@Mapper
public interface UserDTOMapper {
UserDTOMapper MAPPER = Mappers.getMapper( UserDTOMapper.class );
//@Mapping( source = \"test\", target = \"testing\" )
//@Mapping( source = \"test1\", target = \"testing2\" )
UserDTO toTarget( UserDO s );
}
使用:
public static void main( String[] args ) {
//simpleDemo();
UserDO userDO = new UserDO();
userDO.setId(1L);
userDO.setName(\"aihe\");
userDO.setGmtCreate(new Date());
userDO.setGender(0);
userDO.setPassword(\"xxxxxx\");
UserDTO userDTO = UserDTOMapper.MAPPER.toTarget(userDO);
Assert.assertEquals(\"名称未成功拷贝\",userDTO.getName(),\"aihe\");
Assert.assertEquals(\"Id未成功拷贝\", 1L, (long)userDTO.getId());
Assert.assertEquals(\"性别未成功拷贝\", Integer.valueOf(0),userDTO.getGender());
}
常见用法
- 属性类型相同,名称不同的时候,使用@Mapping注解指定source和target字段名称对应关系, 如果有多个这种属性,那就指定多个@Mapping注解。
- 忽略某个字段,在@Mapping的时候,加上ignore = true
- 转化日期格式,字符串到数字的格式,可以使用dateFormat,numberFormat
- 如果有自定义转换的需求,写一个简单的Java类即可,然后在方法上打上Mapstruct的注解@Named,在在@Mapper(uses = 自定义的类),然后@Mapping中用上qualifiedByName。
@Mapping(target = \"userNick1\", source = \"userNick\")
@Mapping(target = \"createTime\", source = \"createTime\", dateFormat = \"yyyy-MM-dd\")
@Mapping(target = \"age\", source = \"age\", numberFormat = \"#0.00\")
@Mapping(target = \"id\", ignore = true)
@Mapping(target = \"userVerified\", defaultValue = \"defaultValue-2\")
UserDTO toTarget( UserDO s );
性能测试
测试代码
import java.util.Date;
import com.alibaba.fastjson.JSON;
import org.junit.Before;
import org.junit.Test;
/**
* @author : aihe aihe.ah@alibaba-inc.com
* @date : 2022/9/12 9:47 AM
* 使用场景:
* 功能描述:
*/
public class BenchDemoTest{
/**
* 转化对象
*/
private UserDO userDO;
/**
* 转化次数
*/
private final static int count = 1000000;
@Before
public void before() {
userDO = new UserDO();
userDO.setId(1L);
userDO.setName(\"aihe\");
userDO.setGmtCreate(new Date());
userDO.setGender(0);
userDO.setPassword(\"xxxxxx\");
}
@Test
public void mapstruct() {
long startTime = System.currentTimeMillis();
for (int i = 1; i <=count; i++) {
UserDTO userDTO = UserDTOMapper.MAPPER.toTarget(userDO);
}
System.out.println(\"mapstruct time\" + (System.currentTimeMillis() - startTime));
}
@Test
public void beanCopier() {
long startTime = System.currentTimeMillis();
for (int i = 1; i <= count; i++) {
UserDTO targetBean = new UserDTO();
BeanCopyUtils.copy(userDO, targetBean);
}
System.out.println(\"beanCopier time\" + (System.currentTimeMillis() - startTime));
}
@Test
public void springBeanUtils(){
long startTime = System.currentTimeMillis();
for (int i = 1; i <=count; i++) {
UserDTO userDTO = new UserDTO();
org.springframework.beans.BeanUtils.copyProperties(userDO, userDTO);
}
System.out.println(\"springBeanUtils time\" + (System.currentTimeMillis() - startTime));
}
@Test
public void fastjson() {
long startTime = System.currentTimeMillis();
for (int i = 1; i <= count; i++) {
JSON.parseObject(JSON.toJSONString(userDO), UserDTO.class);
}
System.out.println(\"fastjson time\" + (System.currentTimeMillis() - startTime));
}
}
测试结果
- 可以看出BeanCopier和MapStruct是远远超过其他转换方式的...
- BeanCopier虽然快,但是比mapstruct还是有20倍的性能差距...
最后
总结下本文的内容:
- 软件系统一般都会进行分层,领域模型也会随之进行分层,即每层都有自己关注的模型对象; 分层的主要原因是便于维护。
- 模型之间的对象经常要互相转换,常用的转换实现有反射和get/set,反射的性能很差不建议使用
- 然后写了基于get/set实现的beancopier和mapstruct使用方式,简单测试了下性能,mapstrcut优于其它各种对象转换方式。并且MapStrcut支持功能更加复杂的对象转换。 性能又好,功能又强大,所以可以考虑优先使用.
:本文采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可, 转载请附上原文出处链接。
1、本站提供的源码不保证资源的完整性以及安全性,不附带任何技术服务!
2、本站提供的模板、软件工具等其他资源,均不包含技术服务,请大家谅解!
3、本站提供的资源仅供下载者参考学习,请勿用于任何商业用途,请24小时内删除!
4、如需商用,请购买正版,由于未及时购买正版发生的侵权行为,与本站无关。
5、本站部分资源存放于百度网盘或其他网盘中,请提前注册好百度网盘账号,下载安装百度网盘客户端或其他网盘客户端进行下载;
6、本站部分资源文件是经压缩后的,请下载后安装解压软件,推荐使用WinRAR和7-Zip解压软件。
7、如果本站提供的资源侵犯到了您的权益,请邮件联系: 442469558@qq.com 进行处理!
猪小侠源码-最新源码下载平台 PHP教程 Java对象转换的方案分享 https://www.20zxx.cn/585509/xuexijiaocheng/qes.html
猪小侠源码,优质资源分享网
相关文章
- java非法字符‘\\ufeff‘解决方法 2024-03-11
- Java中单体应用锁的局限性&分布式锁 2024-03-11
- 如何通过php函数解决页面渲染慢的问题? 2024-03-11
- 如何评估php性能优化函数的效果? 2024-03-11
- 如何利用PHP脚本在Linux中进行目录操作 2024-03-11
- 如何通过PHP脚本在Linux中进行系统监测 2024-03-11
- 如何使用php函数来优化表单处理和提交功能? 2024-03-11
- 如何通过PHP脚本在Linux服务器上实现数据加密 2024-03-11
- 如何通过php函数来优化验证码生成和校验? 2024-03-11
- 如何使用php函数来优化多语言支持功能? 2024-03-11
做猪小侠源码的代理,提供一站式服务
如果你不懂得搭建网站或者服务器,小程序,源码之类的怎么办? 第一通过本站学习各种互联网的技术 第二就是联系客服,我帮帮你搭建(当然要收取部分的费用) 第三成为我们的代理,我们提供整套的服务。