Java 对象转换

76 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情

对象转换

  • 本文将介绍 对象转换 , 在 JavaWeb 开发中我们经常需要对各类对象进行转换(DB对象,DTO,VO等等).
  • 目前解决对象转换的形式有

    • JSON 序列化反序列化 , 存在的问题 字段名称需要一样

    • BeanUtils.copyProperties , 存在的问题 字段名称需要一样

    • 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);
}
  • 代码部分这里仅仅是一个接口, 由开发者自定义实现即可.

后续要做的事情就是如何把 ConvertFacadeConvert 串起来.

笔者使用的方式是将 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> 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(), "张三");

    }



}