Java对象转换的思考
对象转换 本文将介绍 对象转换 , 在 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 下面是注册的完整代码, 请各位自行阅读 <details> <summary>register</summary> 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); } } } </details> 万事俱备 最后只差 ConvertFacade 的调用了. 调用也就是去找 map 容器中的 convert 对象并且执行方法convert public static <T> T convert(Object source, Class<T> 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(), "张三"); } }