对象转换
- 本文将介绍 对象转换 , 在 JavaWeb 开发中我们经常需要对各类对象进行转换(DB对象,DTO,VO等等).
- 目前解决对象转换的形式有
- JSON 序列化反序列化 , 存在的问题 字段名称需要一样
- BeanUtils.copyProperties , 存在的问题 字段名称需要一样
- mapstruct, 存在的问题多个接口需要大量记忆
- 有关 mapstruct 的使用文章可以查看 这篇文章
tips: 上述观点仅是笔者所认为的, 并不一定都是问题. 同时欢迎各位读者指出还有那些工具.
笔者的转换
下面将给各位介绍笔者认为比较好的一种对象转换方式. 先说说笔者提出的这个方式的缺点.
- 需要手动注册. (本文并没有采取包扫描的形式进行因此需要手动注册)
- 需要手动编写 convert 逻辑. 笔者并不认为这是一个缺点. 对比 mapstruct 发现有下面的代码
- convert 接口的实现类很多.
@Mappings({ @Mapping(source = "id", target = "userId"), @Mapping(source = "name", target = "userName") }) UserVO4 toConvertVO(User source);
User fromConvertEntity(UserVO4 userVO4);
笔者所提出的这个设计是将这部分注解形式的代码使用 java 编码
下面开始正式的实现.
实现
首先提出问题
- 面对 mapstruct 的使用来说需要记忆大量的转换类(A的转换接口是那个), 这部分记忆工作很大. 笔者认为应该从一个方法直接得到. 而不是去代码中检索各类转换器然后确定是否使用. 针对这个问题笔者定义出下面方法
public class ConvertFacade {
public static <T> T convert(Object source, Class<T> target) { return null; }
}
解释: 希望通过一个原始对象+目标对象的类型直接得到结果.
在这个基础上我们还需要做出 convert 的逻辑. 前文说到笔者将 mapstruct 的注解转换成了代码, 下面来对这部分进行一个分析
public interface Convert<S, T> {
/**
* 转换
* @param source 原始对象
* @return 转换结果对象
*/
T convert(S source);
}
- 代码部分这里仅仅是一个接口, 由开发者自定义实现即可.
后续要做的事情就是如何把 ConvertFacade
和 Convert
串起来.
笔者使用的方式是将 s , t 两个泛型的类(Class) 获取将他们组成一个对象 ConvertSourceAndTarget
, 将 ConvertSourceAndTarget
对象和 当前的 convert 实现类进行绑定 即容器 static Map<ConvertSourceAndTarget, Convert> convertMap = new ConcurrentHashMap<>(256);
public class ConvertSourceAndTarget {
private Class<?> sourceTypeClass;
private Class<?> targetTypeClass;
}
在数据存储方式决定后, 需要制作一个可以放入到容器中的方法
register
下面是注册的完整代码, 请各位自行阅读
register
public class DefaultConvertRegister implements ConvertRegister {
private static final Logger log = LoggerFactory.getLogger(DefaultConvertRegister.class);
static Map<ConvertSourceAndTarget, Convert> convertMap
= new ConcurrentHashMap<>(256);
public static Convert getConvertMap(ConvertSourceAndTarget param) {
if (log.isInfoEnabled()) {
log.info("getConvertMap,param = {}", param);
}
if (param == null) {
return null;
}
else if (convertMap.containsKey(param)) {
return convertMap.get(param);
}
return null;
}
@Override
public void register(Convert convert) {
if (convert == null) {
log.warn("当前传入的convert对象为空");
return;
}
Class<? extends Convert> convertClass = convert.getClass();
handler(convert, convertClass);
}
private void handler(Convert convert, Class<? extends Convert> convertClass) {
Type[] genericInterfaces = convertClass.getGenericInterfaces();
for (Type genericInterface : genericInterfaces) {
ParameterizedType pType = (ParameterizedType) genericInterface;
boolean equals = pType.getRawType().equals(Convert.class);
if (equals) {
Type[] actualTypeArguments = pType.getActualTypeArguments();
if (actualTypeArguments.length == 2) {
Type a1 = actualTypeArguments[0];
Type a2 = actualTypeArguments[1];
try {
Class<?> sourceClass = Class.forName(a1.getTypeName());
Class<?> targetClass = Class.forName(a2.getTypeName());
ConvertSourceAndTarget convertSourceAndTarget =
new ConvertSourceAndTarget(sourceClass,
targetClass);
// 如果类型相同 覆盖
convertMap.put(convertSourceAndTarget, convert);
}
catch (Exception e) {
log.error("a1=[{}]", a1);
log.error("a2=[{}]", a2);
log.error("从泛型中转换成class异常", e);
}
}
}
}
}
@Override
public void register(Class<? extends Convert> convert) throws IllegalAccessException, InstantiationException {
if (convert == null) {
log.warn("当前传入的convert对象为空");
return;
}
Convert cv = convert.newInstance();
if (cv != null) {
handler(cv, convert);
}
}
}
万事俱备 最后只差 ConvertFacade 的调用了. 调用也就是去找 map 容器中的 convert 对象并且执行方法
convert
public static
T convert(Object source, Class target) { if (log.isInfoEnabled()) { log.info("convert,source = {}, target = {}", source, target); } if (source == null || target == null) { throw new IllegalArgumentException("参数异常请重新检查"); } ConvertSourceAndTarget convertSourceAndTarget = new ConvertSourceAndTarget(source.getClass(), target); Convert convert = DefaultConvertRegister.getConvertMap(convertSourceAndTarget); if (convert != null) { return (T) convert.convert(source); } return null; }
测试
首先准备 Source 、Target 、Convert 对象
source 对象
public class S { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
target 对象
public class T { private String username; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
convert 接口的实现
public class C1 implements Convert<S, T> { public C1() { } @Override public T convert(S source) { T t = new T(); t.setUsername(source.getName()); return t; } }
测试用例
public class DefaultConvertRegisterTest { ConvertRegister convertRegister = new DefaultConvertRegister(); @Test public void register() { C1 c1 = new C1(); convertRegister.register(c1); S s = new S(); s.setName("张三"); T convert = ConvertFacade.convert(s, T.class); Assert.assertEquals(convert.getUsername(), "张三"); } @Test public void register2() throws InstantiationException, IllegalAccessException { convertRegister.register(C1.class); S s = new S(); s.setName("张三"); T convert = ConvertFacade.convert(s, T.class); Assert.assertEquals(convert.getUsername(), "张三"); } }