Web 开发的时候,我们往往需要构建 VO 进行返回数据给前端,那么如何给 VO 赋值呢?如果是一个对象的话,我们可以使用 SpringBoot 提供的 BeanUtils.copyProperties(source, target) 这种方式,但是如果我们需要给一个数组或者是 List 进行批量赋值的话,BeanUtils 是不能满足的,这种情况下可以使用 DozerBeanMapper 进行属性拷贝。
dozer是一个能把实体和实体之间进行转换的工具.只要建立好映射关系.就像是ORM的数据库和实体映射一样。dozer的功能比BeanUtils功能更强大,但是BeanUtils的性能更好。所以简单的同名同类型属性赋值转换使用BeanUtils,复杂的级联结构的属性赋值转换使用Dozer
- Dozer可以实现Integer、Long等基础类型与String数据类型的属性之间的转换(只要名字相同就可以了,数据类型可以不同),BeanUtils只能做到同数据类型同名的属性之间赋值。
- Dozer可以实现递归级联结构的对象赋值,BeanUtils(Spring包下面的)也可以
- Dozer可以实现复杂的数据转换关系,通过xml配置的方式,BeanUtils做不到
dozer是用来两个对象之间属性转换的工具,有了这个工具之后,我们将一个对象的所有属性值转给另一个对象时,就不需要再去写重复的set和get方法了。DozerMapper相比较其他的对象转换工具,性能适中,配置比较简单便捷。看看DozerMapper的使用过程
1.引入相关的maven依赖
<dependency>
<groupId>com.github.dozermapper</groupId>
<artifactId>dozer-spring-boot-starter</artifactId>
<version>6.5.2</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.core</artifactId>
<version>6.0.0</version>
</dependency>
2.配置相关的xml文件
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping
http://dozermapper.github.io/schema/bean-mapping.xsd">
<configuration>
<stop-on-errors>false</stop-on-errors>
<date-format>yyyy-MM-dd HH:mm:ss</date-format>
<wildcard>true</wildcard>
<trim-strings>true</trim-strings>
<!-- LocalDateTime和String转换 -->
<custom-converters>
<converter type="com.test.ft.common.dozer.StringDateConvert">
<class-a>java.time.LocalDateTime</class-a>
<class-b>java.lang.String</class-b>
</converter>
<converter type="com.test.ft.common.dozer.DateToLocalDateConvert">
<class-a>java.util.Date</class-a>
<class-b>java.time.LocalDateTime</class-b>
</converter>
</custom-converters>
<copy-by-references>
<copy-by-reference>java.time.*</copy-by-reference>
</copy-by-references>
</configuration>
</mappings>
3.编写相关的配置类,读取xml的配置
import com.github.dozermapper.core.DozerBeanMapperBuilder;
import com.github.dozermapper.core.Mapper;
import com.github.dozermapper.spring.DozerBeanMapperFactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author aaa
* @date 2023-10-24 16:59
* @description Dozer Mapper 配置类
*/
@Configuration
public class DozerConfig {
@Bean
public Mapper getMapper(@Value("classpath*:/mapping/*.xml") Resource[] resources) {
List<String> list = Arrays.stream(resources).map(r -> "mapping/" + r.getFilename()).collect(Collectors.toList());
return DozerBeanMapperBuilder.create().withMappingFiles(list).build();
}
@Bean
public DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean(@Value("classpath*:/mapping/*.xml") Resource[] resources) throws IOException {
final DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean = new DozerBeanMapperFactoryBean();
dozerBeanMapperFactoryBean.setMappingFiles(resources);
return dozerBeanMapperFactoryBean;
}
}
4.相关工具类代码
package com.test.ft.common.dozer;
import com.github.dozermapper.core.DozerBeanMapperBuilder;
import com.github.dozermapper.core.Mapper;
import com.github.dozermapper.core.MapperModelContext;
import com.github.dozermapper.core.metadata.MappingMetadata;
import com.test.ft.common.assrt.MyAssert;
import org.springframework.util.Assert;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author tencent wb
* @date 2023-10-24 17:18
* @description
*/
public class DozerUtil {
private static final Mapper MAPPER = DozerBeanMapperBuilder.buildDefault();
private DozerUtil() {
}
public static <T, S> T convert(S source, Class<T> target) {
Assert.notNull(source, "source can not be null");
Assert.notNull(target, "target can not be null");
return MAPPER.map(source, target);
}
private static <T, S> Collection<T> collectionConvert(Collection<S> sourceList, Class<T> target) {
Assert.notEmpty(sourceList, "source can not be null");
MyAssert.notNull(target,"target can not be null");
Stream<S> stream = Optional.of(sourceList).orElse(Collections.emptyList()).stream().filter(Objects::nonNull);
if (sourceList instanceof Set) {
return stream.map(e -> MAPPER.map(e, target)).collect(Collectors.toSet());
}
return stream.map(e -> MAPPER.map(e, target)).collect(Collectors.toList());
}
public static <T, S> Set<T> setConvert(Set<S> sourceList, Class<T> target) {
return (Set<T>)collectionConvert(sourceList, target);
}
public static <T, S> List<T> listConvert(List<S> sourceList, Class<T> target) {
return (List<T>)collectionConvert(sourceList, target);
}
}
String和LocalDateTime转换,编写一个StringDateConvert.java,这个路径要配置在dozer-mapping.xml 中,代码如下:
package com.test.ft.common.dozer;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.github.dozermapper.core.DozerConverter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* @author aaa
* @description Dozer 时间类型和字符串类型转换
*/
public class StringDateConvert extends DozerConverter<String, LocalDateTime> {
public StringDateConvert(Class<String> prototypeA, Class<LocalDateTime> prototypeB) {
super(prototypeA, prototypeB);
}
@Override
public LocalDateTime convertTo(String s, LocalDateTime localDateTime) {
// 使用Hutool的时间工具类来转换
return LocalDateTimeUtil.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
@Override
public String convertFrom(LocalDateTime localDateTime, String s) {
// 使用Hutool的时间工具类来转换
s = LocalDateTimeUtil.format(localDateTime, "yyyy-MM-dd HH:mm:ss");
return s;
}
}
其他的类型转换可以参照时间转换,继承DozerConverter后重写对应的方法即可
5.测试结果
编写测试类:
测试结果:
附录,项目基本结构:
需要注意的是:Java8 的LocalDateTime可以convert成String,但是String数据不能convert成LocalDateTime