共计 4938 个字符,预计需要花费 13 分钟才能阅读完成。
反射是 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 工具类,又毁灭了扩散在各个类中的垃圾代码。
文章演示代码:点击跳转