java 8 反射:如何迭代私有最终 java.util.Vector 字段

问题描述

我想通过 Java 反射获取对类中定义的 java.util.Vector 私有变量的引用并对其进行迭代。类中变量的声明如下:private final Vector<Class<?>> classes = new Vector<>();

实际上,我试图阅读 Vector 的课程是这样的:java.lang.ClassLoader

我需要的对象来自于此(我需要使用 getSuperclass() 两次,因为在应用程序服务器中 ClassLoader.getSystemClassLoader() 返回一个特定于 weblogic 的类加载器类,而我需要的字段来自父类):

ClassLoader.getSystemClassLoader().getClass().getSuperclass().getSuperclass()

以及调试模式下的字段,我需要:

enter image description here

关于上下文的一点:java 反射代码将在一个简单的 servlet 中运行,部署到应用服务器。

我可以使用以下代码获取 java.lang.reflect.Field

Class c = ClassLoader.getSystemClassLoader().getClass().getSuperclass().getSuperclass();
Field classesField = c.getDeclaredField("classes");
classesField.setAccessible(true);
Type type = classesField.getGenericType();
System.out.println(type.getTypeName()); //java.util.Vector

结果:

enter image description here

但我无法获得从 java.lang.reflect.Fieldjava.util.Vector 的引用,以便我可以从中读取项目。

我试过这样的方法

// 1:
Vector v = new Vector<>();
// ERROR: Can not set final java.util.Vector field java.lang.classLoader.classes to java.util.Vector

// 2:
public static class VectorWrapper {
    private Vector<Class<?>> v = new Vector<>();
}
VectorWrapper v = new VectorWrapper();
// ERROR: Can not set final java.util.Vector field java.lang.classLoader.classes to a.b.c.Test$VectorWrapper

// try to get the vector
Object o = classesField.get(v);
Vector classes = (Vector<Class<?>>) o;
System.out.println(classes.size());

上面的代码不起作用,抛出异常。

似乎是因为类中的字段定义为final,所以我无法获取引用。也许我需要获得 Vector 的迭代器而不是 Vector 本身?但我不确定。

如何在此 Vector 字段上循环并从中获取项目?

-- 更新 --

我创建了一个简单的测试应用程序,没有应用程序服务器,只使用纯Java。我尝试过 Java 11,但那台机器上只有这个 Java 版本。但我想这与 Java 8 的行为相同。只需复制并粘贴代码,然后执行它即可查看异常:

Can not set final java.util.Vector field java.lang.classLoader.classes to java.util.Vector

代码

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Vector;

public class ReflectionTest {

    public static void main(String[] args) throws Exception {
        Class c = ClassLoader.getSystemClassLoader().getClass().getSuperclass().getSuperclass().getSuperclass();
        printFields(c);
        printValue(c,"classes");
    }

    private static void printFields(Class c) {
        System.out.println(c.getTypeName());
        int i = 0;
        Field[] fields = c.getDeclaredFields();
        for (Field m : fields) {
            i++;
            System.out.println(i + ".  " + m.getType().getName() + ":" + m.getName());
        }
        System.out.println("-------------------------------------");
    }

    private static void printValue(Class c,String fieldName) throws Exception {
        Field classesField = c.getDeclaredField(fieldName);
        classesField.setAccessible(true);
        Type type = classesField.getGenericType();
        System.out.println(type.getTypeName());

        Vector v = new Vector<>();
        Object o = classesField.get(v);
        Vector classes = (Vector<Class<?>>) o;
        System.out.println(classes.size());
    }
}

解决方法

您需要保留对 ClassLoader 对象的引用,以便您可以调用 classesField.get(classLoader)。以下是一些可能有用的示例代码:

@Test
public void testVector() throws Exception {
    ClassLoader classLoader = ClassLoader.getSystemClassLoader();
    Class classLoaderClass = null;
    for (Class c = classLoader.getClass(); c != Object.class; c = c.getSuperclass()) {
        System.out.println(c.getName());
        if (c == java.lang.ClassLoader.class) {
            classLoaderClass = c;
        }
    }
    System.out.println("found a " + classLoaderClass.getName());
    Field classesField = classLoaderClass.getDeclaredField("classes");
    classesField.setAccessible(true);
    Type type = classesField.getGenericType();
    System.out.println(type.getTypeName()); //java.util.Vector
    Object o = classesField.get(classLoader);
    System.out.println("o is a " + o.getClass().getName());
    if (o instanceof Vector) {
        Vector v = (Vector) o;
        v.forEach(e -> {
            System.out.println(e);
        });
    }
}

更短的版本:

@Test
public void testVector2() throws Exception {
    Field classesField = java.lang.ClassLoader.class.getDeclaredField("classes");
    classesField.setAccessible(true);
    ClassLoader classLoader = ClassLoader.getSystemClassLoader();
    Vector<Class<?>> v = (Vector<Class<?>>) classesField.get(classLoader);

    System.out.println("ClassLoader has " + v.size() + " classes");
}