ReflectionUtils反射工具:精要介绍与实战应用指南

简介: ReflectionUtils反射工具:精要介绍与实战应用指南

一、ReflectionUtils工具类介绍

org.springframework.util.ReflectionUtils 是 Spring 框架提供的一个反射工具类,它封装了 Java 反射 API 的一些常用操作,使得我们能够更加方便、简洁地使用反射功能。。

1. 获取 Class 对象

ReflectionUtils 提供了多种获取 Class 对象的方法,如:

  • findClass(String name): 根据类名获取 Class 对象,与 Class.forName(String) 类似,但会缓存已经加载过的类。
  • getClass(Object obj): 获取对象的运行时类,与 obj.getClass() 等效。
  • getUserClass(Object obj): 如果对象是代理对象,则获取其背后的实际类(即目标类),否则与 getClass(Object) 相同。

2. 访问字段(Field)

这个工具类也简化了对字段的访问操作:

  • findField(Class<?> clazz, String name): 在给定的类及其父类中查找具有指定名称的字段。
  • findField(Class<?> clazz, String name, Class<?> type): 根据名称和类型查找字段。
  • getField(Field field): 获取可访问的字段对象,如果字段是私有的,会设置其为可访问。
  • setField(Object obj, String name, Object value): 设置指定对象的字段值。
  • getFieldValue(Object obj, String name): 获取指定对象的字段值。

3. 调用方法(Method)

ReflectionUtils 同样提供了方法来简化方法的调用:

  • findMethod(Class<?> clazz, String name, Class<?>... paramTypes): 查找具有指定名称和参数类型的方法。
  • invokeMethod(Method method, Object target): 调用无参数方法。
  • invokeMethod(Method method, Object target, Object... args): 调用带参数的方法。
  • invokeJdbcMethod(Method method, Object target, Object... args): 专为 JDBC 方法设计的调用,处理 SQL 异常。
  • makeAccessible(Method method): 确保方法可以访问,即使它是私有的。

4. 构造函数和实例化

这个工具类还提供了通过构造函数创建类实例的方法:

  • getConstructorIfAvailable(Class<?> clazz, Class<?>... paramTypes): 尝试获取指定参数类型的构造函数,如果找不到则返回 null
  • instantiateClass(Constructor<?> ctor, Object... args): 使用指定的构造函数和参数实例化类。
  • newInstance(Class<?> clazz, Object... args): 简化版的实例化方法,它内部会查找合适的构造函数并调用 instantiateClass

5. 其他实用方法

  • isAssignable(Class<?> lhsType, Class<?> rhsType): 判断一个类是否可以赋值给另一个类(考虑继承关系)。
  • doesMethodDeclareException(Method method, Class<?> exceptionType): 检查方法是否声明了指定类型的异常。
  • getUniqueDeclaredMethods(Class<?> leafClass): 获取类中声明的所有方法,包括继承的方法,但排除重写的方法,确保每个方法只出现一次。
  • getAllDeclaredMethods(Class<?> leafClass): 获取类中声明的所有方法,包括继承的方法,但不排除重写的方法。
  • getDeclaredMethods(Class<?> clazz): 获取类中直接声明的所有方法,不包括继承的方法。
  • getDeclaredFields(Class<?> clazz): 获取类中直接声明的所有字段。

注意事项

虽然 ReflectionUtils 提供了很多便捷的方法,但使用反射仍然需要谨慎。反射操作可能会破坏封装性、增加性能开销,并可能引发安全问题。因此,在不需要动态访问的情况下,最好避免使用反射。

二、ReflectionUtils源码解读

以下是 ReflectionUtils 类中部分方法的代码:

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.springframework.lang.Nullable;

public abstract class ReflectionUtils {

    // 缓存已解析的类
    private static final Class<?>[] EMPTY_CLASS_ARRAY = {};

    // ... 其他常量和方法 ...

    // 获取类的方法,处理了类加载异常
    @Nullable
    public static Class<?> findClass(String name) throws ClassNotFoundException {
        // 省略了缓存逻辑和类加载器的详细处理
        return Class.forName(name, false, getDefaultClassLoader());
    }

    // 判断方法是否声明了某个异常
    public static boolean doesMethodDeclareException(Method method, Class<?> exceptionType) {
        Class<?>[] declaredExceptions = method.getExceptionTypes();
        for (Class<?> declaredException : declaredExceptions) {
            if (declaredException.isAssignableFrom(exceptionType)) {
                return true;
            }
        }
        return false;
    }

    // 使字段可访问,并返回字段对象
    public static Field getField(Field field) {
        makeAccessible(field);
        return field;
    }

    // 设置字段的值
    public static void setField(Object obj, String fieldName, @Nullable Object value) {
        Field field = findField(obj.getClass(), fieldName);
        if (field == null) {
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
        }
        makeAccessible(field);
        try {
            field.set(obj, value);
        } catch (IllegalAccessException ex) {
            throw new IllegalStateException("Cannot access field [" + fieldName + "] on target [" + obj + "]", ex);
        }
    }

    // 获取字段的值
    @Nullable
    public static Object getFieldValue(Object obj, String fieldName) {
        Field field = findField(obj.getClass(), fieldName);
        if (field == null) {
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
        }
        makeAccessible(field);
        try {
            return field.get(obj);
        } catch (IllegalAccessException ex) {
            throw new IllegalStateException("Cannot access field [" + fieldName + "] on target [" + obj + "]", ex);
        }
    }

    // 查找类中的字段
    @Nullable
    public static Field findField(Class<?> clazz, String name) {
        return findField(clazz, name, null);
    }

    // 查找类中的字段,考虑字段类型
    @Nullable
    public static Field findField(Class<?> clazz, String name, @Nullable Class<?> type) {
        Class<?> searchType = clazz;
        while (searchType != null && searchType != Object.class) {
            Field[] fields = searchType.getDeclaredFields();
            for (Field field : fields) {
                if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) {
                    return field;
                }
            }
            searchType = searchType.getSuperclass();
        }
        return null;
    }

    // ... 省略了其他方法 ...

    // 设置可访问性的通用方法
    public static void makeAccessible(AccessibleObject accessibleObject) {
        if ((!Modifier.isPublic(accessibleObject.getModifiers()) ||
                !Modifier.isPublic(accessibleObject.getDeclaringClass().getModifiers())) &&
                !accessibleObject.isAccessible()) {
            accessibleObject.setAccessible(true);
        }
    }

    // ... 省略了其他方法 ...
}

以上代码只是一个丐版,并不是 ReflectionUtils 类的完整实现。

三、ReflectionUtils的使用

获取类的私有字段值

有一个类 Person,它有一个私有字段 name,想要通过反射来获取这个私有字段的值。

import org.springframework.util.ReflectionUtils;

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    // 省略其他方法...
}

public class ReflectionExample {
    public static void main(String[] args) {
        Person person = new Person("John Doe");
        String fieldName = "name";

        // 使用 ReflectionUtils 获取私有字段的值
        String nameValue = (String) ReflectionUtils.getFieldValue(person, fieldName);
        System.out.println("Name value: " + nameValue); // 输出: Name value: John Doe
    }
}

设置类的私有字段值

想要通过反射来设置私有字段 name 的值,可以这样:

public class ReflectionExample {
    public static void main(String[] args) {
        Person person = new Person("John Doe");
        String fieldName = "name";
        String newValue = "Jane Doe";

        // 使用 ReflectionUtils 设置私有字段的值
        ReflectionUtils.setField(person, fieldName, newValue);

        // 再次获取并验证字段值已更改
        String updatedValue = (String) ReflectionUtils.getFieldValue(person, fieldName);
        System.out.println("Updated name value: " + updatedValue); // 输出: Updated name value: Jane Doe
    }
}

调用私有方法

假设 Person 类有一个私有方法 sayHello(),想要通过反射来调用这个方法。

import org.springframework.util.ReflectionUtils;

public class Person {
    // ... 省略其他字段和方法 ...

    private void sayHello() {
        System.out.println("Hello, I'm a private method!");
    }
}

public class ReflectionExample {
    public static void main(String[] args) {
        Person person = new Person("John Doe");
        String methodName = "sayHello";

        // 使用 ReflectionUtils 调用私有方法(无参数)
        ReflectionUtils.invokeMethod(ReflectionUtils.findMethod(Person.class, methodName), person);
        // 输出: Hello, I'm a private method!
    }
}

检查方法是否声明了特定异常

想要检查一个方法是否声明了抛出某个特定类型的异常,可以使用 doesMethodDeclareException 方法。

import org.springframework.util.ReflectionUtils;
import java.io.IOException;

public class ExampleWithException {
    public void methodThatThrows() throws IOException {
        // ... 方法实现 ...
    }
}

public class ReflectionExample {
    public static void main(String[] args) {
        boolean declaresException = ReflectionUtils.doesMethodDeclareException(
                ReflectionUtils.findMethod(ExampleWithException.class, "methodThatThrows"), IOException.class);
        System.out.println("Declares IOException: " + declaresException); // 输出: Declares IOException: true
    }
}
相关文章
|
12月前
|
设计模式 架构师 Java
阿里P8架构师都要学习研究的java加强版23种设计模式神级PDF文档
说在前面的话 Java作为老牌纯正的编程语言,在规范性上有着天然优势。因此本版的设计模式讲解全部用Java语言来描述,并针对Java语言的特性对讲解内容做了相当大的改动。 不知道大家是否听过编程界的一段话:掌握设计模式相当于华山派的"气宗",是程序员的内功修为,虽然在同样的学习时间下,类似Python这种"剑宗"的开发模式见效更快,但是长远来看,"气宗"才是走向软件架构师以上级别的必由之路。 所以,掌握气宗就掌握了编程命脉,然而学习设计模式有四大境界: 接下来给大家分享的就是java溢彩加强版大话设计模式包含的内容知识点。 总目录 主要内容 本文是百万销量的经典畅销书《
197 0
|
2月前
|
Java 程序员
"Java程序员必备秘籍:Lambda表达式如何让你的代码瘦身90%?揭秘简化编程的终极奥秘!"
【8月更文挑战第19天】Java持续进化,Lambda表达式自Java 8起赋予其新活力。它简化代码,使编程更愉悦。以前,简单功能需冗长代码,如列表排序要用匿名内部类实现`Comparator`。现在一行Lambda足矣。Lambda如`(参数) -&gt; {表达式}`,支持零或多参数。
10 0
|
4月前
|
存储 算法 编译器
程序与技术分享:C++模板元编程简介
程序与技术分享:C++模板元编程简介
34 0
|
5月前
|
Java
从零开始学习 Java:简单易懂的入门指南之反射(三十八)
从零开始学习 Java:简单易懂的入门指南之反射(三十八)
|
5月前
|
Go
反射机制全解析:揭秘语言内幕
反射机制全解析:揭秘语言内幕
40 0
|
算法 安全 测试技术
嵌入式软件测试笔记2 |TEmb方法概述
嵌入式软件测试笔记2 |TEmb方法概述
129 0
|
SQL 监控 Java
魔法反射--java反射进阶(实战篇)
相信很多人在初学反射的时候也都会有这个想法(我就不相信就只有我一个人这么蠢!!) 大多数人不了解反射的原因并不是不了解, 而是不知道它到底能用来干什么 今天就来为大家分享一下反射的用法
74 0
|
XML 安全 IDE
JavaSE:第十三章:一分钟了解反射
JavaSE:第十三章:一分钟了解反射
130 0
JavaSE:第十三章:一分钟了解反射
|
Java 数据库连接 mybatis
【SSM框架解析】——前篇:详解动态代理【案例驱动】(案例源码自取)
今天开始要学习SSM框架了,新的开始我决定用博客来记录自己的学习过程,和CSDN上的大佬们一同进步。
【SSM框架解析】——前篇:详解动态代理【案例驱动】(案例源码自取)
|
Java Android开发 数据库管理
Android插件化开发基础之Java反射机制研究(1)
Android插件化开发基础之Java反射机制研究(1)
119 0
下一篇
无影云桌面