Dozer
Dozer是一种Java Bean到Java Bean的映射器,递归地将数据从一个对象复制到另一个对象,它是一个强大的,通用的,灵活的,可重用的和可配置的开源映射框架。
常用于:
- 代码层与层之间javabean转换, 如dao层PO转前端VO
- 分布式中, DAO层PO转DTO, DO 以及web层DTO转VO
注意的场景:
- 由于bean之间的深度复制, 在进行一些类似更新, 插入操作时尤其要注意最终接收到PO的一些关键字段如ID是否是真正需要的. 场景: 传入的DTO A为查出的DTO B复制后的, 这时候A里会有B的ID, 在插入A的时候很有可能造成主键冲突.
建议:
- 不用Dozer最好, Dozer带来的是性能开销.(这是不可能…)
- 某些特殊操作可以用切面控制特殊字段进行置空操作
SpringBoot整合Dozer
jar依赖引入
pom.xml加入以下依赖
<!-- 深度复制: Dozer可以灵活的对对象进行转换,且使用简单-->
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer-spring</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.5.1</version>
<exclusions>
<exclusion>
<artifactId>jcl-over-slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
dozer配置xml引入
resource文件夹下新建dozer文件夹, 并新建bean-mappings.xml, global-configuration.xml
bean-mappings.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net
http://dozer.sourceforge.net/schema/beanmapping.xsd">
</mappings>
global-configuration.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net
http://dozer.sourceforge.net/schema/beanmapping.xsd">
<configuration>
<date-format>yyyy-MM-dd HH:mm:ss</date-format>
<wildcard>true</wildcard>
<trim-strings>false</trim-strings>
<!-- 自定义的枚举与Integer转换器, 下节介绍 -->
<!--<custom-converters>
<converter type="com.dongao.beacon.ds.rest.admin.tookit.EnumIntegerBiDirectionalDozerConverter">
<class-a>java.lang.Enum</class-a>
<class-b>java.lang.Integer</class-b>
</converter>
</custom-converters>-->
</configuration>
</mappings>
自定义DozerConverter,实现CustomConverter并重写convert
枚举转换与Integer转换, 只是简单的把枚举index与Integer转换, 同理枚举转字符串只是转的name. 复杂枚举name, code, dispalyname的转换就不适用默认规则了, 这时需要自定义转换器.
public class EnumIntegerBiDirectionalDozerConverter implements CustomConverter {
@Override
public Object convert(Object destination, Object source, Class<?> destinationClass, Class<?> sourceClass) {
if (EmptyUtils.isEmpty(source)) {
return null;
}
if (source instanceof Enum) {
return getInteger(destinationClass, source);
} else if (source instanceof Integer) {
return getEnum(destinationClass, source);
} else {
throw new MappingException(new StringBuilder("Converter ").append(this.getClass().getSimpleName())
.append(" was used incorrectly. Arguments were: ").append(destinationClass.getClass().getName())
.append(" and ").append(source).toString());
}
}
private Object getInteger(Class<?> destinationClass, Object source) {
try {
Enum<?> em = (Enum<?>) source;
Class<?> clazz = em.getDeclaringClass();
Method getCode = clazz.getMethod("getCode");
Object code = getCode.invoke(source);
return Integer.valueOf((String) code);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private Object getEnum(Class<?> destinationClass, Object source) {
try {
Method m = destinationClass.getDeclaredMethod("valueOfCode", String.class);
Object enumeration = m.invoke(destinationClass.getClass(), String.valueOf(source));
return enumeration;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Dozer的JavaConfig
用于SpringBoot寻找DozerBeanMapperFactoryBean的配置
新建DozerMapperConfig.java
@Configuration
public class DozerMapperConfig {
@Bean
public DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean(@Value("classpath*:dozer/*.xml" ) Resource[] resources) throws Exception {
final DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean = new DozerBeanMapperFactoryBean();
dozerBeanMapperFactoryBean.setMappingFiles(resources);
return dozerBeanMapperFactoryBean;
}
/* @Bean
public IGenerator ejbGenerator() {
return new EJBGenerator();
}*/
}
格式化工厂
建议新建包专门放置Dozer工具
接口定义: 新建IGenerator.java接口
public interface IGenerator {
/**
* 转换
*
* @param s 数据对象
* @param clz 复制目标类型
* @return {@link T}
* @Description: 单个对象的深度复制及类型转换,vo/domain , po
* @author banjuer@outlook.com
* @Time 2018年5月9日 下午3:53:24
*/
<T, S> T convert(S s, Class<T> clz);
/**
* @Description: 深度复制结果集(ResultSet为自定义的分页结果集)
* @param s 数据对象
* @param clz 复制目标类型
* @return
* @author banjuer@outlook.com
* @Time 2018年5月9日 下午3:53:24
*/
//<T, S> Result<T> convert(Result<S> s, Class<T> clz);
/**
* 转换
* @param s 数据对象
* @param clz 复制目标类型
* @return {@link List<T>}
* @Description: list深度复制
* @author banjuer@outlook.com
* @Time 2018年5月9日 下午3:54:08
*/
<T, S> List<T> convert(List<S> s, Class<T> clz);
/**
*
* @param s
* @param clz
* @param <T>
* @param <S>
* @return
*/
<T, S> Paging<T> convertPaging(Paging<S> s, Class<T> clz);
/**
* @param s 数据对象
* @param clz 复制目标类型
* @return
* @Description: set深度复制
* @author banjuer@outlook.com
* @Time 2018年5月9日 下午3:54:39
*/
<T, S> Set<T> convert(Set<S> s, Class<T> clz);
/**
* @param s 数据对象
* @param clz 复制目标类型
* @return
* @Description: 数组深度复制
* @author banjuer@outlook.com
* @Time 2018年5月9日 下午3:54:57
*/
<T, S> T[] convert(S[] s, Class<T> clz);
/**
* 分页信息转换
* @return {@link PageResult<T>}
*/
<T, S> PageResult<T> convertPageInfo(PageInfo<S> s, Class<T> clz);
}
IGenerator实现
@Component
@Lazy(true)
public class EJBGenerator implements IGenerator {
@Autowired
protected Mapper dozerMapper;
@Override
public <T, S> T convert(final S s, Class<T> clz) {
return s == null ? null : this.dozerMapper.map(s, clz);
}
@Override
public <T, S> List<T> convert(List<S> s, Class<T> clz) {
return s == null ? null : s.stream().map(vs -> this.dozerMapper.map(vs, clz)).collect(Collectors.toList());
}
@Override
public <T, S> Paging<T> convertPaging(Paging<S> paging, Class<T> clz) {
Paging<T> pagingVo=new Paging<T>();
pagingVo.setRecords(convert(paging.getRecords(),clz));
pagingVo.setTotal(paging.getTotal());
return pagingVo;
}
@Override
public <T, S> Set<T> convert(Set<S> s, Class<T> clz) {
return s == null ? null : s.stream().map(vs -> this.dozerMapper.map(vs, clz)).collect(Collectors.toSet());
}
@Override
public <T, S> T[] convert(S[] s, Class<T> clz) {
if (s == null) {
return null;
}
@SuppressWarnings("unchecked")
T[] arr = (T[]) Array.newInstance(clz, s.length);
for (int i = 0; i < s.length; i++) {
arr[i] = this.dozerMapper.map(s[i], clz);
}
return arr;
}
@Override
public <T, S> PageResult<T> convertPageInfo(PageInfo<S> s, Class<T> clz) {
return new PageResult(s.getTotal(), convert(s.getList(), clz));
}
}
demo使用
使用Demo
一般在公共父类中引入, 此处例子为前端公共Controller引入
@Controller
public class BaseController {
@Autowired
protected EJBGenerator ejbGenerator = new EJBGenerator();
}
// 个人信息变更记录session
SessionUserDetails userDetails = ejbGenerator.convert(userVo, SessionUserDetails.class);
网友评论