通过反射访问 classLoader 字段

kos*_*ta5 3 java reflection classloader byte-buddy

我们有一个带有自定义类加载器的应用程序,我需要访问给定类的 classLoader 字段。但是,无法通过反射访问此字段:-(

JavaDoc forjava.lang.Class很清楚:

// This field is filtered from reflection access, i.e. getDeclaredField
// will throw NoSuchFieldException
Run Code Online (Sandbox Code Playgroud)

所以这就是我在调用时getDeclaredField("classLoader") 得到的结果 可以以某种方式获得(我看到 IntelliJ 调试以某种方式这样做;如何?)

也许是一些byteBuddy诡计?

Raf*_*ter 6

回答您的问题:您仍然可以通过创建类的镜像来使用 Byte Buddy 进入该字段,该镜像将具有类似的类布局,以便您可以Unsafe通过首先创建隐藏类的镜像来访问和修改字段来自反射的字段:

Class<?> mirror = new ByteBuddy()
    .with(TypeValidation.DISABLED)
    .redefine(Class.class)
    .name("mirror.Class")
    .noNestMate()
    .make()
    .load(null)
    .getLoaded();

Class<?> unsafeType = Class.forName("sun.misc.Unsafe");
Field theUnsafe = unsafeType.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Object unsafe = theUnsafe.get(null);

long offset = (Long) unsafeType
    .getMethod("objectFieldOffset", Field.class)
    .invoke(unsafe, mirror.getDeclaredField("classLoader"));
ClassLoader loader = (ClassLoader) unsafeType
    .getMethod("getObject", Object.class, long.class)
    .invoke(unsafe, Foo.class, offset - 4);
Run Code Online (Sandbox Code Playgroud)

镜像具有与原始类相似的字段布局,以便您可以根据需要保留该布局和访问字段。您可以以相同的方式使用putObject来覆盖字段值。

但是,我会推荐这种方法吗?绝对不。这也将在任何未来版本的 Java 中停止工作。如果您需要一些额外的时间来处理适当的解决方案,这可能是一种可行的方法,但从长远来看,您应该重构代码以使这种变通方法变得不必要。

  • 虽然我真的很喜欢镜像类的技巧,但它在这种情况下似乎不起作用。镜像类将*不*具有相同的布局。HotSpot 对几个引导类(包括 java .lang.Class`,以便直接在 JVM 内访问它们的字段。但使用 ByteBuddy 生成的镜像类不会被特殊对待。这就是您示例中的“- 4”的来源。 (2认同)