对象转换
- 本文将介绍 对象转换 , 在 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 {
1public static <T> T convert(Object source, Class<T> target) { 2 return null; 3}}
-
解释: 希望通过一个原始对象+目标对象的类型直接得到结果.
在这个基础上我们还需要做出 convert 的逻辑. 前文说到笔者将 mapstruct 的注解转换成了代码, 下面来对这部分进行一个分析
1public interface Convert<S, T> { 2 /** 3 * 转换 4 * @param source 原始对象 5 * @return 转换结果对象 6 */ 7 T convert(S source); 8}
- 代码部分这里仅仅是一个接口, 由开发者自定义实现即可.
后续要做的事情就是如何把 ConvertFacade 和 Convert 串起来.
笔者使用的方式是将 s , t 两个泛型的类(Class) 获取将他们组成一个对象 ConvertSourceAndTarget , 将 ConvertSourceAndTarget 对象和 当前的 convert 实现类进行绑定 即容器 static Map<ConvertSourceAndTarget, Convert> convertMap = new ConcurrentHashMap<>(256);
1public class ConvertSourceAndTarget { 2 private Class<?> sourceTypeClass; 3 4 private Class<?> targetTypeClass; 5}
-
在数据存储方式决定后, 需要制作一个可以放入到容器中的方法
register -
下面是注册的完整代码, 请各位自行阅读
</details>1public class DefaultConvertRegister implements ConvertRegister { 2 3 4 private static final Logger log = LoggerFactory.getLogger(DefaultConvertRegister.class); 5 6 static Map<ConvertSourceAndTarget, Convert> convertMap 7 = new ConcurrentHashMap<>(256); 8 9 10 public static Convert getConvertMap(ConvertSourceAndTarget param) { 11 if (log.isInfoEnabled()) { 12 log.info("getConvertMap,param = {}", param); 13 } 14 if (param == null) { 15 return null; 16 } 17 else if (convertMap.containsKey(param)) { 18 return convertMap.get(param); 19 } 20 return null; 21 } 22 23 @Override 24 public void register(Convert convert) { 25 26 if (convert == null) { 27 log.warn("当前传入的convert对象为空"); 28 return; 29 } 30 31 32 Class<? extends Convert> convertClass = convert.getClass(); 33 34 handler(convert, convertClass); 35 36 } 37 38 private void handler(Convert convert, Class<? extends Convert> convertClass) { 39 Type[] genericInterfaces = convertClass.getGenericInterfaces(); 40 41 for (Type genericInterface : genericInterfaces) { 42 ParameterizedType pType = (ParameterizedType) genericInterface; 43 boolean equals = pType.getRawType().equals(Convert.class); 44 if (equals) { 45 Type[] actualTypeArguments = pType.getActualTypeArguments(); 46 47 if (actualTypeArguments.length == 2) { 48 Type a1 = actualTypeArguments[0]; 49 Type a2 = actualTypeArguments[1]; 50 51 try { 52 53 Class<?> sourceClass = Class.forName(a1.getTypeName()); 54 Class<?> targetClass = Class.forName(a2.getTypeName()); 55 56 ConvertSourceAndTarget convertSourceAndTarget = 57 new ConvertSourceAndTarget(sourceClass, 58 targetClass); 59 // 如果类型相同 覆盖 60 convertMap.put(convertSourceAndTarget, convert); 61 } 62 catch (Exception e) { 63 log.error("a1=[{}]", a1); 64 log.error("a2=[{}]", a2); 65 log.error("从泛型中转换成class异常", e); 66 } 67 } 68 } 69 } 70 } 71 72 73 @Override 74 public void register(Class<? extends Convert> convert) throws IllegalAccessException, InstantiationException { 75 if (convert == null) { 76 log.warn("当前传入的convert对象为空"); 77 return; 78 } 79 80 Convert cv = convert.newInstance(); 81 82 if (cv != null) { 83 handler(cv, convert); 84 } 85 86 } 87 88}
-
万事俱备 最后只差 ConvertFacade 的调用了. 调用也就是去找 map 容器中的 convert 对象并且执行方法
convertpublic static <T> T convert(Object source, Class<T> target) { if (log.isInfoEnabled()) { log.info("convert,source = {}, target = {}", source, target); }
1 if (source == null || target == null) { 2 throw new IllegalArgumentException("参数异常请重新检查"); 3 } 4 5 6 ConvertSourceAndTarget convertSourceAndTarget = new ConvertSourceAndTarget(source.getClass(), target); 7 8 Convert convert = DefaultConvertRegister.getConvertMap(convertSourceAndTarget); 9 if (convert != null) { 10 11 return (T) convert.convert(source); 12 } 13 return null; 14}
测试
-
首先准备 Source 、Target 、Convert 对象
-
source 对象
public class S { private String name;
1public String getName() { 2 return name; 3} 4 5public void setName(String name) { 6 this.name = name; 7}}
-
target 对象
public class T { private String username;
1public String getUsername() { 2 return username; 3} 4 5public void setUsername(String username) { 6 this.username = username; 7}}
-
convert 接口的实现
public class C1 implements Convert<S, T> { public C1() { }
1@Override 2public T convert(S source) { 3 T t = new T(); 4 t.setUsername(source.getName()); 5 return t; 6}}
-
测试用例
public class DefaultConvertRegisterTest { ConvertRegister convertRegister = new DefaultConvertRegister();
1@Test 2public void register() { 3 4 C1 c1 = new C1(); 5 convertRegister.register(c1); 6 7 8 S s = new S(); 9 s.setName("张三"); 10 11 T convert = ConvertFacade.convert(s, T.class); 12 Assert.assertEquals(convert.getUsername(), "张三"); 13 14} 15 16 17@Test 18public void register2() throws InstantiationException, IllegalAccessException { 19 20 convertRegister.register(C1.class); 21 22 23 S s = new S(); 24 s.setName("张三"); 25 26 T convert = ConvertFacade.convert(s, T.class); 27 Assert.assertEquals(convert.getUsername(), "张三"); 28 29}}