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
    }
}
相关文章
|
设计模式 架构师 Java
阿里P8架构师都要学习研究的java加强版23种设计模式神级PDF文档
说在前面的话 Java作为老牌纯正的编程语言,在规范性上有着天然优势。因此本版的设计模式讲解全部用Java语言来描述,并针对Java语言的特性对讲解内容做了相当大的改动。 不知道大家是否听过编程界的一段话:掌握设计模式相当于华山派的"气宗",是程序员的内功修为,虽然在同样的学习时间下,类似Python这种"剑宗"的开发模式见效更快,但是长远来看,"气宗"才是走向软件架构师以上级别的必由之路。 所以,掌握气宗就掌握了编程命脉,然而学习设计模式有四大境界: 接下来给大家分享的就是java溢彩加强版大话设计模式包含的内容知识点。 总目录 主要内容 本文是百万销量的经典畅销书《
239 0
|
3月前
|
自然语言处理 安全 PHP
深入浅出PHP编程:从基础到实战
【10月更文挑战第36天】本文将带你走进PHP的奇妙世界,无论你是初学者还是有一定经验的开发者,都将从中获益。文章首先介绍PHP的基础概念和语法,然后通过实际代码示例,展示如何利用PHP进行高效的Web开发。最后,我们将探讨一些高级主题,如面向对象编程、数据库操作以及安全性问题。让我们一起开启PHP的学习之旅吧!
|
5月前
|
PHP 开发者
PHP编程中的面向对象基础与实践
【9月更文挑战第27天】在PHP的海洋里,面向对象编程(OOP)是一艘强大的船,它不仅能让代码组织得更加优雅,还能提高开发效率。本文将带你领略OOP的魅力,从基础概念到实际应用,让你轻松驾驭这艘船,开启高效编程之旅。
|
6月前
|
存储 开发者 Ruby
【揭秘Ruby高手秘籍】OOP编程精髓全解析:玩转类、继承与多态,成就编程大师之路!
【8月更文挑战第31天】面向对象编程(OOP)是通过“对象”来设计软件的编程范式。Ruby作为一种纯面向对象的语言,几乎所有事物都是对象。本文通过具体代码示例介绍了Ruby中OOP的核心概念,包括类与对象、继承、封装、多态及模块混合,展示了如何利用这些技术更好地组织和扩展代码。例如,通过定义类、继承关系及私有方法,可以创建具有特定行为的对象,并实现灵活的方法重写和功能扩展。掌握这些概念有助于提升代码质量和可维护性。
50 0
|
6月前
|
开发者 C# Android开发
明白吗?Xamarin与Native的终极对决:究竟哪种开发方式更适合您的项目需求,让我们一探究竟!
【8月更文挑战第31天】随着移动应用开发的普及,开发者面临多种技术选择。本文对比了跨平台解决方案Xamarin与原生开发方式的优势与劣势。Xamarin使用C#进行跨平台开发,代码复用率高,可大幅降低开发成本;但因基于抽象层,可能影响性能。原生开发则充分利用平台特性,提供最佳用户体验,但需维护多套代码库,增加工作量。开发者应根据项目需求、团队技能和预算综合考量,选择最适合的开发方式。
145 0
|
9月前
|
程序员 Python
类的设计奥秘:从代码到架构的科普全解
类的设计奥秘:从代码到架构的科普全解
108 2
|
9月前
|
Java
从零开始学习 Java:简单易懂的入门指南之反射(三十八)
从零开始学习 Java:简单易懂的入门指南之反射(三十八)
|
存储 算法 Java
合工大实验一 Java开发环境使用与面向对象编程
合工大实验一 Java开发环境使用与面向对象编程
175 0
|
设计模式 XML 人工智能
太牛了!Android开发高级工程师实战手写框架
导语 又到了金九银十的面试季,自己也不得不参与到这场战役中来,其实是从去年底就开始看,Android的好机会确实不太多,但也还好,3年+的android开发经历还是有一些面试机会的,不过确实不像几年前门槛那么低了,总的体会就是小的创业公司比较注重你的项目经历是否和自己的贴合,直接能过来独当一面。 大厂除了看中项目经历外,还比较注重你知识面的广度,是广度、深度和解决方案等多方面的考察,平时够工作要好好积累临时刷题只聊点皮毛估计是过不了关的。下面就总结一些大厂面试遇到必定会问的知识点,我把网络上讲解的最好的视频给大家整理出来了,各种风格的都有。
太牛了!Android开发高级工程师实战手写框架
|
Java Android开发 索引
Android插件化开发基础之Java反射机制研究(2)
Android插件化开发基础之Java反射机制研究(2)
143 0