在 Java 开发领域,反射机制犹如一把神奇的钥匙,能够解锁许多隐藏在代码深处的功能,为开发者提供极大的灵活性与强大的动态编程能力。今天,我们将聚焦于一款精心打造的 ReflectUtil
反射工具类,深入剖析其内部精妙之处,助力大家在 Java 编程的进阶之路上大步前行。
一、工具类概览与基石搭建
ReflectUtil
类位于 com.fink.smallscale.core.utils
包下,引入了强大的日志记录工具 org.apache.logging.log4j
,通过静态常量 logger
精准记录工具类运行中的各类关键信息,无论是调试阶段的细节追踪,还是运行时异常排查,都能让开发者一目了然。这一坚实的日志基础,为整个工具类的稳定运行与问题诊断保驾护航。
二、核心方法深度洞察
(一)属性访问器的反射调用
invokeGetter(T target, String fieldName)
方法:
public static <T> Object invokeGetter(T target, String fieldName)
throws NoSuchMethodException, SecurityException,
IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
// 如果属性名为xxx,则方法名为getXxx
String methodName = "get" + StringUtils.firstCharUpperCase(fieldName);
Method method = target.getClass().getMethod(methodName);
return method.invoke(target);
}
此方法专为调用对象属性的 getter
方法而生。当给定一个目标对象 target
与属性名 fieldName
,它巧妙地按照 JavaBean 的命名规范,动态拼接出对应的 get
方法名(如属性 name
对应 getName
),再利用 getMethod
从类的方法列表中精准获取该方法,最后通过 invoke
触发执行,返回属性值。在实际场景中,若需动态获取对象的某个私有属性值用于业务逻辑判断,此方法便能轻松穿越访问限制,大放异彩。
invokeSetter(T target, String fieldName, Object args)
方法:
public static <T> void invokeSetter(T target, String fieldName, Object args)
throws NoSuchFieldException, SecurityException,
NoSuchMethodException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
// 如果属性名为xxx,则方法名为setXxx
String methodName = "set" + StringUtils.firstCharUpperCase(fieldName);
Class<?> clazz = target.getClass();
Field field = clazz.getDeclaredField(fieldName);
Method method = clazz.getMethod(methodName, field.getType());
method.invoke(target, args);
}
与 invokeGetter
相辅相成,该方法用于调用 setter
方法。首先依据属性名推导出 set
方法名,接着获取目标类的字节码信息,定位到对应的属性字段与方法,确保参数类型匹配无误后,借助 invoke
将传入的参数 args
设置到目标对象的指定属性上。想象一下,在数据批量填充场景下,需要动态为对象的多个属性赋值,它便能高效地完成使命,避免繁琐的手工赋值。
(二)成员信息检索利器
getAllFields(Class<?> clazz)
方法:
public static List<Field> getAllFields(Class<?> clazz) throws Exception {
List<Field> fieldList = new ArrayList<>();
Class<?> tempClass = clazz;
while (tempClass!= null) {
//当父类为null的时候说明到达了最上层的父类(Object类).
fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
//得到父类,然后赋给自己
tempClas)
tempClass = tempClass.getSuperclass();
}
return fieldList;
}
这是一个强大的字段信息收集器,它能够遍历类的继承层级,将指定类及其所有父类的成员字段一网打尽,存入列表返回。在进行复杂的对象序列化、数据映射或者框架底层的元数据提取时,全面掌握类的所有字段信息至关重要,此方法无疑提供了极大的便利,让开发者无需逐个类去翻查,一步到位获取完整的字段集。
getField(Class<?> parameterObjectClass, String propertyName)
方法:
public static Field getField(Class<?> parameterObjectClass, String propertyName) throws Exception {
Field field = null;
List<Field> fieldList = getAllFields(parameterObjectClass);
for (Field f : fieldList) {
if (propertyName.equals(f.getName())) {
field = f;
}
}
return field;
}
基于 getAllFields
的强大功能,该方法进一步聚焦,在给定类及其所有父类的字段集合中,依据属性名精准定位并返回特定的字段对象。比如在进行数据校验时,需要针对某个特定属性进行规则检查,通过它能迅速锁定目标字段,展开后续的校验逻辑。
(三)方法的动态探寻与调用
getMethod(Class<?> clz, String methodName, Class<?>... parameterTypes)
方法:
public static Method getMethod(Class<?> clz, String methodName, Class<?>... parameterTypes) {
Method method = null;
try {
method = clz.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
Class<?> superClass = null;
if (clz.isInterface()) {
Class<?>[] interfaces = clz.getInterfaces();
if (interfaces.length == 1) {
superClass = interfaces[0];
}
} else {
superClass = clz.getSuperclass();
}
if (superClass == null || superClass == Object.class) {
return null;
}
method = getMethod(superClass, methodName, parameterTypes);
}
return method;
}
当我们需要在运行时动态获取某个类的特定方法时,此方法便是得力助手。它首先尝试从目标类直接获取指定名称与参数类型匹配的方法,若遭遇 NoSuchMethodException
,则智能判断类的类型(接口或普通类),进而向其父类或实现的接口中层层探寻,直至找到目标方法或确定不存在为止。这种动态查找机制在插件式架构开发、动态代理场景下,为灵活调用不同实现类的同名方法提供了关键支持。
invokeGetterNew(T target, String fieldName)
方法:
public static <T> Object invokeGetterNew(T target, String fieldName) {
PropertyDescriptor descriptor = null;
Object obj = null;
try {
descriptor = new PropertyDescriptor(fieldName, target.getClass());
//得到get方法
Method readMethod = descriptor.getReadMethod();
//调用get方法得到返回值并返回
obj = readMethod.invoke(target);
} catch (Exception e) {
logger.error("调用getter方法出现异常,属性名为:{}{}", fieldName, e.getMessage());
}
return obj;
}
这是另一种风格的 getter
方法调用途径,它借助 PropertyDescriptor
类,根据属性名与目标类构建描述符,进而从中获取对应的读方法(get
方法)并执行,返回属性值。相较于直接通过方法名反射调用,这种方式更贴合 JavaBean 的属性访问规范,在一些遵循标准 JavaBean 设计模式的框架集成场景下,兼容性更佳,能有效避免因方法命名不规范等问题导致的调用失败。
invokeSetterNew(T target, String fieldName, Object arg)
方法:
public static <T> void invokeSetterNew(T target, String_auxiliary_1, String_auxiliary_2, Object arg) {
PropertyDescriptor descriptor = null;
Object obj = null;
try {
descriptor = new PropertyDescriptor(fieldName, target.getClass());
//得到setter方法
Method writeMethod = descriptor.getWriteMethod();
//得到参数类型。注意,这里只考虑一个参数的情况
Class<?> paramClazz = writeMethod.getParameterTypes()[0];
//调用set方法
writeMethod.invoke(target, arg);
} catch (Exception e) {
logger.error("调用setter方法出现异常,属性名为:{}", fieldName);
}
}
类似地,该方法采用 PropertyDescriptor
实现 setter
方法的调用,通过构建描述符获取写方法(set
方法),并根据参数类型校验后执行赋值操作。在处理遵循 JavaBean 规范的属性设置时,它提供了一种简洁、规范的实现方式,确保在复杂的对象属性更新场景下,数据能够准确无误地注入。
(四)突破 final 属性限制的大胆尝试
public static Object declaredFieldUpdate(Object object, final String fieldName, Object newValue) throws Exception {
// 获得属性对象 其可能被final修饰
Field prosField = object.getClass().getDeclaredField(fieldName);
// 此时copyOnThreadLocal的modifier的值是17 = 16 + 1 (16代表是final,1代表是public)
// 以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符
// 获得Field类的字节码对象,然后获取它的modifiers属性
Field modifiers = Field.class.getDeclaredField("modifiers");
// modifiers属性是field类私有的,需要设置setAccessible(true)
modifiers.setAccessible(true);
// 开始手动修改此属性的修饰符 ------------------------------
// ~按位取反 去掉 final 修饰
modifiers.setInt(prosField, modifiers.getInt(prosField) & (~Modifier.FINAL));
// 将final属性剔除以后的modifier值
//去掉JVM约束
prosField.setAccessible(true);
//读取之前的值
Object oldValue = prosField.get(object);
// 修改属性的值 设置为 阿里的线程本地存储
prosField.set(object, newValue);
return oldValue;
}
在 Java 语言中,final
属性通常被视为不可逾越的红线,一旦赋值便不可更改。然而,ReflectUtil
中的 declaredFieldUpdate
方法却剑走偏锋,为特殊场景提供了一种突破限制的可能。它首先获取目标对象中被 final
修饰的属性字段对象,接着通过反射深入 Field
类的底层,巧妙利用位运算修改字段的修饰符标志,去除 final
限制,随后再次利用反射读写属性值,实现对 final
属性的更新。但需注意,这种操作违背了常规的编程语义,应谨慎使用,仅在诸如某些极端的框架底层优化、调试场景下,当确有必要打破常规时,它才能发挥关键作用。
三、实战演练:代码示例点亮智慧之光
public class ReflectUtilDemo {
private static class Person {
private String name;
private int age;
private final String id = "123456";
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getId() {
return id;
}
}
public static void main(String[] args) throws Exception {
Person person = new Person("John", 30);
// 调用getter方法示例
String name = (String) ReflectUtil.invokeGetter(person, "name");
System.out.println("Name: " + name);
// 调用setter方法示例
ReflectUtil.invokeSetter(person, "age", 31);
int newAge = (int) ReflectUtil.invokeGetter(person, "age");
System.out.println("New Age: " + newAge);
// 获取所有成员字段示例
List<Field> fields = ReflectUtil.getAllFields(Person.class);
for (Field field : fields) {
System.out.println("Field: " + field.getName());
}
// 获取特定成员字段示例
Field ageField = ReflectUtil.getField(Person.class, "age");
if (ageField!= null) {
ageField.setAccessible(true);
int ageValue = (int) ageField.get(person);
System.out.println("Age Value: " + ageValue);
}
// 动态获取并调用方法示例
Method greetMethod = ReflectUtil.getMethod(Person.class, "greet", String.class);
if (greetMethod!= null) {
greetMethod.invoke(person, "Hello");
}
// 另一种风格的getter调用示例
String nameNew = (String) ReflectUtil.invokeGetterNew(person, "name");
System.out.println("Name New: " + nameNew);
// 另一种风格的setter调用示例
ReflectUtil.invokeSetterNew(person, "age", 32);
int newAgeNew = (int) ReflectUtil.invokeGetterNew(person, "age");
System.out.println("New Age New: " + newAgeNew);
// 修改final属性示例(谨慎使用)
String oldId = (String) ReflectUtil.declaredFieldUpdate(person, "id", "654321");
System.out.println("Old Id: " + oldId);
String newId = (String) ReflectUtil.invokeGetterNew(person, "id");
System.out.println("New Id: " + newId);
}
public static void greet(String message) {
System.out.println(message + ", I'm a person.");
}
}
上述示例代码构建了一个简单的 Person
类,全方位展示了 ReflectUtil
工具类的各个方法实战应用。从基本的属性访问、成员字段获取,到动态方法调用,再到挑战常规的 final
属性修改,每一个步骤都清晰地呈现出工具类如何在实际场景中发挥强大的动态编程能力,为大家理解与运用提供了直观的参考。
四、总结与升华
ReflectUtil
工具类作为 Java 反射技术的集大成者,为开发者打开了一扇通往动态编程世界的大门。通过封装一系列实用且强大的反射操作方法,它不仅简化了复杂的反射代码编写过程,降低了开发门槛,还在众多高级编程场景下,如框架开发、动态代理、插件扩展等,提供了不可或缺的支持。然而,反射虽强大,但也犹如一把双刃剑,过度或不当使用可能导致性能损耗、代码可读性变差等问题。因此,在享受反射带来的便利时,开发者务必结合具体场景,权衡利弊,精准施策,让 ReflectUtil
在最合适的地方绽放光芒,助力 Java 项目开发迈向新的高度。
希望这篇深度剖析的博客能够帮助大家深入理解 ReflectUtil
工具类,掌握 Java 反射技术的精髓,在今后的编程之旅中,凭借这一利器披荆斩棘,创造更多精彩!