关于java:Java高级特性反射使用反射把对象转换成-MongoDb-的结构

反射是 Java 的一个高级技巧,大量地用在各种开源我的项目上。比方,Spring、Tomcat、Jetty 等等我的项目中,都大量地用到了反射。

作为 Java 程序员,咱们如果用好反射,岂但能进步本人的技术水平,还能开发出更好的我的项目。

然而,尽管很多人据说过反射,但却不晓得应该用在哪里。

那么,咱们就从理论工作登程,应用反射,把对象转换成 MongoDb 的数据结构。当你在搞懂这个例子后,就能明确反射是怎么个用法。

需要剖析

在电商零碎中,一些数据要保留到 MongoDb 中,以此来进步查问的性能。但在此之前,咱们必须把数据先转换成 MongoDb 的构造,也就是把 Java 对象转换成 Document。

比方,订单信息要存到 MongoDb 中,就得把订单对象转换成 Document。

可这样一来,每个实体类都得开发一个 2Doc() 办法。这个办法毫无技术含量,就是把各种字段 put 到 Document 外面。而且一旦字段多了,一不留神就会写错代码,你感受一下。

public class Order {
    private Long id;
    private Long userId;
    private String orderNo;
    private BigDecimal amount;
    private String createTime;
    private String updateTime;
    // 省略无数字段
    
    // 转换方法:订单转doc
    public Document order2Doc(Order order) {
        Document doc = new Document();

        doc.put("id", order.getId());
        doc.put("userId", order.getUserId());
        doc.put("orderNo", order.getOrderNo());
        doc.put("amount", order.getAmount());
        doc.put("createTime", order.getCreateTime());
        doc.put("updateTime", order.getUpdateTime());
        // 省略有数put...

        return doc;
    }
}

除此之外,咱们还得从 MongoDb 中取数据,把 Document 转换回 Java 对象,你再感受一下。

public class Order {
    private Long id;
    private Long userId;
    private String orderNo;
    private BigDecimal amount;
    private String createTime;
    private String updateTime;
    // 省略无数字段
    
    // 转换方法:doc转订单
    public Order doc2Order(Document doc) {
        Order order = new Order();
        
        order.setId((Long) doc.get("id"));
        order.setUserId((Long) doc.get("userId"));
        order.setOrderNo((String) doc.get("orderNo"));
        order.setAmount((BigDecimal) doc.get("amount"));
        order.setCreateTime((String) doc.get("createTime"));
        order.setUpdateTime((String) doc.get("updateTime"));
        // 省略有数set...

        return order;
    }
}

光是一个订单类都这么麻烦了,何况这样的类不止一个,而且我的项目总有新需要,如果一个字段改了,那你麻烦大了,说不定要把整个我的项目翻一遍。

因而,为了少出错,必须优化这两个转换方法,而这次优化用到了 Java 的两个高级个性:反射、泛型。为了让大家更直观的理解,我将分成两个版本迭代。

第一版,利用反射,简化实体类的转换方法;

第二版,利用泛型、反射,提取 MongoDb 工具类;

接下来,咱们就一步步迭代吧~

利用反射,简化实体类的转换方法

在第一版的迭代中,咱们要简化实体类的两个转换方法。

咱们先从 Java 对象转 Document 开始,还是以 Order 类为例。

首先,咱们通过反射,获取到订单类的所有字段信息;而后,应用循环遍历这些字段;最初,在循环中,咱们放开字段的拜访权限,把字段 put 到 Document 外面。

public class Order {
    // ...省略无数字段

    public Document order2Doc(Order order) throws Exception {
        Document doc = new Document();

        // 获取所有字段:通过 getClass() 办法获取 Class 对象,而后获取这个类所有字段
        Field[] fields = order.getClass().getDeclaredFields();
        for (Field field : fields) {
            // 凋谢字段操作权限
            field.setAccessible(true);
            // 设置值
            doc.put(field.getName(), field.get(order));
        }

        return doc;
    }
}

你能够看到,通过反射革新后,代码简略了很多。一个对象无论有多少个字段,要写多少 put 操作,只有这几行代码就能搞定。Java 对象转成 MongoDb 的构造,看起来也不那么麻烦了。

照着这个思路,咱们再来革新第二个办法,Document 转 Java 对象。

public class Order {
    // ...省略无数字段

    public Order doc2Order(Document doc) throws Exception {
        Order order = new Order();

        for (String key : doc.keySet()) {
            // 获取字段
            Field field = order.getClass().getDeclaredField(key);
            // 凋谢字段操作权限
            field.setAccessible(true);
            // 设置值
            field.set(order, doc.get(key));
        }

        return order;
    }
}

首先,咱们应用循环遍历 Document;在循环中,应用反射获取相应的字段,再放开字段的拜访权限,把 Document 的值设置到对象的字段里。

到了这儿,咱们利用反射,简化了两个实体类的转换方法,第一版的迭代根本实现了。剩下的工作,就是复制粘贴,把各个类从新革新一遍。

然而,通过这一版迭代,尽管缩小了很多工作,但仍然有很多不合理的中央。

首先,反复代码还是很多。每个实体类都有两个转换方法,但这两个办法的外围逻辑是一样的,齐全没必要到处复制。

而后,这不是实体类应该承当的性能。实体类只负责短暂保留数据,不负责任何长久化性能。你把数据存到哪里,该转换成什么数据结构,这和实体类没什么关系。

换句话说,咱们还得做第二次迭代。

利用泛型、反射,提取 MongoDb 工具类

简略来说,泛型是一种格调或范式,你不必一开始就指明具体的参数类型,而是在应用的时候再确定参数类型。

如果把泛型、反射联合在一起,能帮咱们缩小很多反复代码。

咱们来看看,该怎么做第二次迭代?

先从 Java 对象转 Document 开始。咱们先申明一个泛型办法;而后,通过反射,获取泛型类的所有字段信息,再应用循环遍历这些字段;最初,在循环中,把字段 put 到 Document 外面。

public class MongoDbUtils {

    // 定义泛型办法:
    // 1. 在返回值前,申明泛型参数 <参数名>;
    // 2. 传入参数时,指定一个泛型参数
    public static <T> Document obj2Doc(T obj) throws Exception {
        Document doc = new Document();

        // 获取所有字段:通过 getClass() 办法获取 Class 对象,而后获取这个类所有字段
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            // 凋谢字段操作权限
            field.setAccessible(true);
            // 设置值
            doc.put(field.getName(), field.get(obj));
        }

        return doc;
    }
}

在退出泛型后,反复代码大量缩小了,实体类不必再独自写 2Doc()办法了。在应用的时候,只有调用 MongoDbUtils.obj2Doc() 就行。

依照同样的思路,咱们持续来革新第二个办法,Document 转 Java 对象。

public class MongoDbUtils {

    // 定义泛型办法:
    // 1. 在返回值前,申明泛型参数 <参数名>;
    // 2. 传入参数必须是 Class,但这个 Class 是泛型参数,不限度类型
    public static <T> T doc2Obj(Document doc, Class<T> clazz) throws Exception {
        // 实例化泛型对象
        T obj = clazz.newInstance();

        for (String key : doc.keySet()) {
            // 获取字段
            Field field = clazz.getDeclaredField(key);
            // 凋谢字段操作权限
            field.setAccessible(true);
            // 设置值
            field.set(obj, doc.get(key));
        }

        return obj;
    }
}

首先,咱们定义实例化一个泛型对象;而后,咱们应用循环遍历 Document;最初,在循环中,应用反射获取相应的字段,把 Document 的值设置到泛型对象的字段里。

第二版的迭代就根本实现了。咱们在第一版迭代的根底上,退出了泛型,失去了一个工具类 MongoDbUtils,这个工具类失去后果和以前齐全一样,你能够看下测试代码。

public static void main(String[] args) throws Exception {
    Order order = new Order();
    order.setId(0L);
    order.setUserId(0L);
    order.setOrderNo("1");
    order.setAmount(new BigDecimal("0"));
    order.setCreateTime("2");
    order.setUpdateTime("3");
    System.out.println("原始数据:" + order);

    Document document = MongoDbUtils.obj2Doc(order);
    System.out.println("转换doc数据:" + document);

    Order order1 = MongoDbUtils.doc2Obj(document, Order.class);
    System.out.println("转换java数据:" + order1);
}

运行后果:
原始数据:Order(id=0, userId=0, orderNo=1, amount=0, createTime=2, updateTime=3)
转换doc数据:Document{{id=0, userId=0, orderNo=1, amount=0, createTime=2, updateTime=3}}
转换java数据:Order(id=0, userId=0, orderNo=1, amount=0, createTime=2, updateTime=3)

这样一来,咱们就不必保留实体类上的转换方法了,剩下的工作就是删代码。

MongoDb 和 Java 对象的相互转换就实现了。咱们做了两次迭代,第一次迭代利用了反射,把大量手动 set/get 操作给去掉了;第二次迭代在原来的根底上,退出了泛型的利用,又去掉了一堆反复代码。

写在最初

反射是一种动静操作类的机制,也是 Java 的高级个性。

这篇文章应用了反射来操作类,免去了本人手动写 set/get 代码的麻烦,毁灭了一大堆反复代码。

此外,反射要想发挥作用,有时还得配合其它高级个性。比方,这里把反射和泛型联合,提取出了 MongoDb 工具类,又毁灭了扩散在各个类中的垃圾代码。

文章演示代码:点击跳转

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理