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
    }
}
相关文章
|
8月前
|
设计模式 架构师 Java
阿里P8架构师都要学习研究的java加强版23种设计模式神级PDF文档
说在前面的话 Java作为老牌纯正的编程语言,在规范性上有着天然优势。因此本版的设计模式讲解全部用Java语言来描述,并针对Java语言的特性对讲解内容做了相当大的改动。 不知道大家是否听过编程界的一段话:掌握设计模式相当于华山派的"气宗",是程序员的内功修为,虽然在同样的学习时间下,类似Python这种"剑宗"的开发模式见效更快,但是长远来看,"气宗"才是走向软件架构师以上级别的必由之路。 所以,掌握气宗就掌握了编程命脉,然而学习设计模式有四大境界: 接下来给大家分享的就是java溢彩加强版大话设计模式包含的内容知识点。 总目录 主要内容 本文是百万销量的经典畅销书《
160 0
|
2天前
|
存储 算法 编译器
程序与技术分享:C++模板元编程简介
程序与技术分享:C++模板元编程简介
|
1月前
|
Go
反射机制全解析:揭秘语言内幕
反射机制全解析:揭秘语言内幕
28 0
|
11月前
|
Java
【JavaSE专栏23】Java反射有多强? 他拥有这五大神奇功能!
【JavaSE专栏23】Java反射有多强? 他拥有这五大神奇功能!
|
算法 安全 测试技术
嵌入式软件测试笔记2 |TEmb方法概述
嵌入式软件测试笔记2 |TEmb方法概述
110 0
|
设计模式 XML 人工智能
太牛了!Android开发高级工程师实战手写框架
导语 又到了金九银十的面试季,自己也不得不参与到这场战役中来,其实是从去年底就开始看,Android的好机会确实不太多,但也还好,3年+的android开发经历还是有一些面试机会的,不过确实不像几年前门槛那么低了,总的体会就是小的创业公司比较注重你的项目经历是否和自己的贴合,直接能过来独当一面。 大厂除了看中项目经历外,还比较注重你知识面的广度,是广度、深度和解决方案等多方面的考察,平时够工作要好好积累临时刷题只聊点皮毛估计是过不了关的。下面就总结一些大厂面试遇到必定会问的知识点,我把网络上讲解的最好的视频给大家整理出来了,各种风格的都有。
太牛了!Android开发高级工程师实战手写框架
|
Java Android开发 数据库管理
Android插件化开发基础之Java反射机制研究(1)
Android插件化开发基础之Java反射机制研究(1)
111 0
|
Java Android开发 索引
Android插件化开发基础之Java反射机制研究(2)
Android插件化开发基础之Java反射机制研究(2)
120 0
|
设计模式 Java 机器人
21节视频课+超全源码解析多态特性 | Java开发者进阶站
多态如何实现,实现了多态能怎样?如何进行对象的转型?接口又是什么,为什么会有接口,怎样用接口来弥补单继承的不足?这一篇,带你一探究竟!
|
JavaScript Java
Java反射-高级开发必须懂的
理解反射对学习Java框架有很大的帮助,如Spring框架的核心就是使用Java反射实现的,而且对做一些Java底层的操作会很有帮助。 一:Class类的使用 ①.万事万物皆对象,(当然,基本数据类型,静态成员不是面向对象(属于类的)),所以我们创建的每一个类也都是对象,即类本身是java.lang.Class类的实例对象,但是这些对象都不需要new出来,因为java.lang.Class类的构造方法是私有的。
19286 0