1. 什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
通俗点讲,反射就是把java类中的各种成分映射成一个个的Java对象。
例如:类的class声明(class对象),变量(field),方法(method)等等信息,利用反射技术可以对一个类进行解剖,动态获取信息进行处理。
2. 反射的实现
1、获取Class对象的三种方式
1.1 Object ——> getClass();
TextMessage text = new TextMessage();
Class<? extends TextMessage> clazz = text.getClass();
1.2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
Class<TextMessage> clazz = TextMessage.class;
1.3 通过Class类的静态方法:forName(String className)(常用)
Class clazz = Class.forName("com.demo.TextMessage");
3. ReflectionUtils常见方法使用
//通过属性名称name和类型type,从clazz获取field
public static Field findField(Class clazz, String name, Class type)
//通过属性名称name,从clazz获取field
public static Field findField(Class clazz, String name)
//给对应target对象的field赋值value
public static void setField(Field field, Object target, Object value)
//根据属性字段field和对象target,获取的对象中字段的值
public static Object getField(Field field, Object target)
//根据类,方法名称和参数类型查找方法
public static Method findMethod(Class clazz, String name)
//根据类,方法名称和参数类型查找方法
public static Method findMethod(Class clazz, String name, Class[] paramTypes)
//调用target对象的方法,无入参
public static Object invokeMethod(Method method, Object target)
//调用target对象的方法,入参为args
public static Object invokeMethod(Method method, Object target, Object[] args)
//判断属性是否包含特定修饰符
public static boolean isPublicStaticFinal(Field field)
//让属性可以访问,主要针对私有属性
public static void makeAccessible(Field field)
4. ReflectionUtils实现源码demo分析
findField方法根据类型,字段名称和字段类型查询一个字段,主要实现就是遍历的向父类获取clazz所有的field,然后进行name和type的匹配,进而找到对应的field。
public static Field findField(Class<?> clazz, String name, Class<?> type) {
Class<?> searchType = clazz;
while (Object.class != searchType && searchType != null) {
Field[] fields = getDeclaredFields(searchType);
for (Field field : fields) {
if ((name == null || name.equals(field.getName())) &&
(type == null || type.equals(field.getType()))) {
return field;
}
}
searchType = searchType.getSuperclass();
}
return null;
}
private static Field[] getDeclaredFields(Class<?> clazz) {
//对类型和字段做了缓存,保存到了declaredFieldsCache中
Field[] result = declaredFieldsCache.get(clazz);
if (result == null) {
result = clazz.getDeclaredFields();
declaredFieldsCache.put(clazz, (result.length == 0 ? NO_FIELDS : result));
}
return result;
5. ReflectionUtils使用场景
最实用的场景就是,当一个接口或者方法针对不同的场景,需要取不同的对象,进行不同的处理。比如发送消息的接口,针对不同的消息类型,可能要处理的数据不一样,有文本消息,有图片消息,有短链,或者菜单栏消息等等。
//通过定义的type字段作区分
//然后找到type对应的属性字段
//找到对应的field对象
Field field = ReflectionUtils.findField(req.getClass(), typeEnum.getField());
//获取数据并赋值
if (Objects.nonNull(field)) {
//设置为可访问
ReflectionUtils.makeAccessible(field);
//根据field的type解析对应的值
Object msgBody = jsonUtils.readValue(bodyStr, field.getType());
//给对象对应的属性字段赋值
ReflectionUtils.setField(field, req, msgBody);
}