反射相关练习
演示的实体类如下
package com.mongodb.model;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Date;
/**
* 汽车类
* @author: gl_stars
* @data: 2020年 09月 09日 17:39
**/
@Data
@Document(collection = "user")
public class CarDO {
@Id
@Indexed
private Long id ;
/**
* 小汽车名称
*/
private String name ;
/**
* 厂家
*/
private String manufactor ;
/**
* 生产日期
*/
private Date dateOfManufacture ;
}
一、反射获取对象信息
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName(HouseDO.class.getName());
// 这种方式也OK
// ClassLoader loader = Thread.currentThread().getContextClassLoader();
// Class clazz = loader.loadClass(HouseDO.class.getName());
System.out.println("===============本类属性===============");
// 取得本类的全部属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 权限修饰符
int mo = field.getModifiers();
// 访问修饰符
String priv = Modifier.toString(mo);
// 属性类型 输出格式为 class java.lang.Long
Class<?> type = field.getType();
// 数据类型 输出格式为:java.lang.Long
String typeName = type.getName();
// 属性名称
String fieldName = field.getName();
}
System.out.println("==========实现的接口或者父类的属性==========");
// 取得实现的接口或者父类的属性,上面的使用只是方法不同而已。getFields();
Field[] filedFather = clazz.getFields();
for (Field field : filedFather) {
// 权限修饰符
int mo = field.getModifiers();
// 访问修饰符
String priv = Modifier.toString(mo);
// 属性类型 输出格式为 class java.lang.Long
Class<?> type = field.getType();
// 数据类型 输出格式为:java.lang.Long
String typeName = type.getName();
// 属性名称
String fieldName = field.getName();
}
}
二、反射给对象赋值
- 方式一
public static void main(String[] args) throws Exception {
try {
// 通过类装载器获取CarDO类对象
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass(CarDO.class.getName());
// 获取类的默认构造器对象并通过它实例化CarDO
Constructor cons = clazz.getDeclaredConstructor((Class[]) null);
CarDO car = (CarDO) cons.newInstance();
car.setDateOfManufacture(new Date());
car.setId(14656L);
car.setName("比亚迪");
car.setManufactor("比亚迪厂家");
System.out.println(car);
// 输出结果:CarDO(id=14656, name=比亚迪, manufactor=比亚迪厂家, dateOfManufacture=Thu Mar 11 01:20:21 CST 2021)
} catch (Exception e) {
e.printStackTrace();
}
}
- 方式二
public static void main(String[] args) throws Exception {
try {
// 通过类装载器获取CarDO类对象
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass(CarDO.class.getName());
// 获取类的默认构造器对象并通过它实例化CarDO
Constructor cons = clazz.getDeclaredConstructor((Class[]) null);
CarDO car = (CarDO) cons.newInstance();
// 通过反射方法设置属性
Method setBrand = clazz.getMethod("setId", Long.class);
setBrand.invoke(car, 14645646546l);
Method setColor = clazz.getMethod("setName", String.class);
setColor.invoke(car, "长安");
Method setMaxSpeed = clazz.getMethod("setManufactor", String.class);
setMaxSpeed.invoke(car, "我家生产的");
System.out.println(car);
// 输出结果为 :CarDO(id=14645646546, name=长安, manufactor=我家生产的, dateOfManufacture=null)
} catch (Exception e) {
e.printStackTrace();
}
}
三、适用案例(Map集合转为实体对象)
使用MongoDB
朋友都明白,如果我们使用连集合查询的时候需要使用聚合。但是聚合查询有个特点,我们实体类里面的 id
字段会保存在MongoDB
中会做 _id
。但是如果使用聚合查询时,返回给我们的字段依然是 _id
而不是 id
。但是返回来的是一个Map
集合,从表我们可以根据自己定义的kay
直接从Map
集合中获取做一个强转是没有问题的。但是主表有多少个字段就在MongoDB
返回的这个Map
集合中就有多少个key
。总不能让我们一个字段一个字段的去取出来存入对象吧,所以使用反射写一个工具类,直接调用即可将Map
集合转为实体对象。
package com.centre.common.utils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 将Map集合转为对象
* <p>
* 使用范例:
* public static void main(String[] args) throws Exception {
* Map<String,Object> map = new HashMap<>();
* map.put("id",12346546l);
* map.put("name","比亚迪,长安");
* map.put("manufactor","比亚迪厂家,长安厂家");
* map.put("dateOfManufacture",new Date());
*
* HandleMapToObject<CarDO> toObject = new HandleMapToObject<CarDO>();
* CarDO carDO = toObject.mapToObject(map, CarDO.class,true);
* System.out.println(carDO);
* // 输出结果:CarDO(id=12346546, name=比亚迪,长安, manufactor=比亚迪厂家,长安厂家, dateOfManufacture=Thu Mar 11 00:41:32 CST 2021)
* }
* </p>
* @Author: Stars
* @Date: 2021/3/10 22:06
*/
public class HandleMapToObject<T> {
/**
* 将Map集合转为实体对象
* @param map Map集合
* @param t 转换后的对象
* @param considerThread true:考虑现成安全,false:不考虑
* @return
* @throws Exception
*/
private T mapToObject(Map map, Class<T> t,Boolean considerThread) throws Exception {
Class<?> clazz = null;
if (considerThread) {
//通过类装载器获取t类对象
ClassLoader loader = Thread.currentThread().getContextClassLoader();
clazz = loader.loadClass(Class.forName(t.getName()).getName());
} else {
clazz = Class.forName(Class.forName(t.getName()).getName());
}
//获取类的默认构造器对象并通过它实例化t
Constructor cons = clazz.getDeclaredConstructor((Class[]) null);
T t1 = (T) cons.newInstance();
// 获取t对象的所有属性
Field[] fields = clazz.getDeclaredFields();
// 将t对象中属性保存为Map的key值,数据类型保存为value值
Map<String, Object> fieldMap = Arrays.stream(fields).collect(Collectors.toMap(Field::getName, Field::getType));
// 取出map集合中的全部数据
Class<?> finalClazz = clazz;
map.forEach((k, v) -> {
try {
// 给当前属性赋值,本来数据类型应该fieldMap.get(k).getClass()这样写。但是上面获取t对象的数据类型时Field::getType这种方法应用不能再获取getName()属性,所以这里是class java.lang.String的类型了,不能再点getClass()了。 所有强转了一下。(Class) fieldMap.get(k)
Method setBrand = finalClazz.getMethod(initcap(String.valueOf(k)), (Class) fieldMap.get(k));
setBrand.invoke(t1, v);
} catch (Exception e) {
System.out.println("map集合转数组异常:"+t.getClass());
}
});
return t1;
}
/***
* 将对象属性转为set方法名
* @param str 转大写的字符串
* @return
*/
public static String initcap(String str) {
return "set" + str.substring(0, 1).toUpperCase() + str.substring(1) ;
}
}
- 实现思路
将Map
集合名为为map
和实体对象类型传入。创建一个Map
集合保存对象的属性和数类型,Map
集合名为fieldMap
,因为反射赋值时需要使用对象属性和属性类型。将对象的属性保存为fieldMap
集合的key
,数据类型保存为fieldMap
集合的value
。然后循环map
集合,因为MongoDB
返回来的这个map
集合的key
就是对象的属性名。所以循环map
集合,取出key
值到fieldMap
集合中获取数类型。现在属性名、数据类型和保存的数据都知道了,接下来的事情就是水到渠成了。