前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ReflectionUtils反射工具:精要介绍与实战应用指南

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

作者头像
公众号:码到三十五
发布2024-04-02 09:07:53
3500
发布2024-04-02 09:07:53
举报
文章被收录于专栏:设计模式

一、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 类中部分方法的代码:

代码语言:javascript
复制
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,想要通过反射来获取这个私有字段的值。

代码语言:javascript
复制
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 的值,可以这样:

代码语言:javascript
复制
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(),想要通过反射来调用这个方法。

代码语言:javascript
复制
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 方法。

代码语言:javascript
复制
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
    }
}

使用 ReflectionUtils 类来处理常见的反射任务,如获取和设置私有字段的值,以及调用私有方法和检查方法异常声明。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-04-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、ReflectionUtils工具类介绍
    • 1. 获取 Class 对象
      • 2. 访问字段(Field)
        • 3. 调用方法(Method)
          • 4. 构造函数和实例化
            • 5. 其他实用方法
              • 注意事项
              • 二、ReflectionUtils源码解读
              • 三、ReflectionUtils的使用
                • 获取类的私有字段值
                  • 设置类的私有字段值
                    • 调用私有方法
                      • 检查方法是否声明了特定异常
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档