Java 尝试重新定义sun.reflect.GeneratedMethodAccessor 1时,ByteBuddy失败

Java 尝试重新定义sun.reflect.GeneratedMethodAccessor 1时,ByteBuddy失败,java,reflection,bytecode,byte-buddy,Java,Reflection,Bytecode,Byte Buddy,出于好奇,我尝试导出GeneratedMethodAccessor1(使用反射时由JVM生成)的字节码 我尝试通过以下方式获取类的字节码: public class MethodExtractor { public static void main(String[] args) throws Exception { ExampleClass example = new ExampleClass(); Method exampleMethod = Ex

出于好奇,我尝试导出GeneratedMethodAccessor1(使用反射时由JVM生成)的字节码

我尝试通过以下方式获取类的字节码:

public class MethodExtractor {

    public static void main(String[] args) throws Exception {

        ExampleClass example = new ExampleClass();

        Method exampleMethod = ExampleClass.class
                .getDeclaredMethod("exampleMethod");
        exampleMethod.setAccessible(true);

        int rndSum = 0;
        for (int i = 0; i < 20; i++) {
            rndSum += (Integer) exampleMethod.invoke(example);
        }

        Field field = Method.class.getDeclaredField("methodAccessor");
        field.setAccessible(true);
        Object methodAccessor = field.get(exampleMethod);
        Field delegate = methodAccessor.getClass().getDeclaredField("delegate");
        delegate.setAccessible(true);
        Object gma = delegate.get(methodAccessor);

        ByteBuddyAgent.installOnOpenJDK();
        try {
            ClassFileLocator classFileLocator = ClassFileLocator.AgentBased
                    .fromInstalledAgent(gma.getClass().getClassLoader());
            Unloaded<? extends Object> unloaded = new ByteBuddy().redefine(
                    gma.getClass(), classFileLocator).make();
            Map<TypeDescription, File> saved = unloaded.saveIn(Files
                    .createTempDirectory("javaproxy").toFile());
            saved.forEach((t, u) -> System.out.println(u.getAbsolutePath()));
        } catch (IOException e) {
            throw new RuntimeException("Failed to save class to file");
        }
    }
}
基本上,我首先在方法调用上迭代足够的时间,让JVM膨胀方法(生成GeneratedMethodAccessor),然后尝试重新定义类以获得字节码

我尝试了同样的方法来导出生成的代理类,它工作得非常完美。这就是促使我尝试这个的原因

当我尝试使用loadClass方法加载类时,GeneratedMethodAccessor1类的DelegatingClassLoader似乎甚至无法重新加载该类


你知道如何为GeneratedMethodAccessor类检索字节码吗?

首先,
NullPointerException
是一个bug,我刚刚修复了它。加载程序应该抛出一个
IllegalArgumentException
,但它从来没有这么远。谢谢你提醒我这件事

简而言之,巴迪面临的问题是

gma.getClass().getClassLoader().findClass(gma.getClass().getName());
抛出一个
ClassNotFoundException
。这是对访问器类使用
DelegatingClassLoader
的结果。作为一个有根据的猜测,我认为这个类装入器打算从外部屏蔽它的类,以便使它们易于垃圾收集。但是,不允许查找类在某种程度上破坏了
类加载器的契约。除此之外,我假设这个加载例程将被重构,以便在将来某个时候使用JDK的匿名类加载器(类似于表示lambda表达式的类)。奇怪的是,即使我能在发行版中找到它。可能,虚拟机在某些地方专门对待这些加载程序

现在,您可以使用下面的
ClassFileTransformer
,它在类加载器上使用一些反射魔法来定位加载的类,然后提取字节数组。(
ClassFileLocator
接口仅采用名称而不是加载的类,以允许处理卸载的类型,通常情况下总是这样。不知道为什么在这种情况下不起作用。)

class DelegateExtractor扩展了ClassFileLocator.AgentBased{
私有最终类加载器类加载器;
私人最终仪表;
公共委托提取器(类加载器类加载器,检测){
超级(类加载器、仪器);
this.classLoader=classLoader;
本仪器=仪器;
}
@凌驾
公共解析定位(字符串类型名){
试一试{
ExtractionClassFileTransformer classFileTransformer=
新的ExtractionClassFileTransformer(classLoader,typeName);
试一试{
仪表.addTransformer(classFileTransformer,true);
//开始恶作剧
Field Field=ClassLoader.class.getDeclaredField(“类”);
字段。setAccessible(true);
指令插入.重新传输类(
(Class)((Vector)field.get(classLoader)).get(0));
//结束讨厌的黑客行为
byte[]binaryRepresentation=classFileTransformer.getBinaryRepresentation();
返回binaryRepresentation==null
?解析.非法.实例
:新分辨率。显式(二进制表示);
}最后{
仪表.拆卸变压器(classFileTransformer);
}
}捕获(忽略异常){
返回Resolution.unliquel.INSTANCE;
}
}
}

为了进一步简化您的代码,您可以直接使用
ClassFileLocator
s,而不是应用重写。事实上,重写可能会稍微修改类文件,即使您没有对类应用任何更改。

您确定JVM会生成真正的类吗?它可能是一种没有相关字节码的本机内部代码。它生成字节码,因为这是膨胀的关键,因此jit可以内联方法调用,因此消除了调用的开销。请看下面的答案,了解为什么这种特殊行为是必要的:-类加载行为是一种黑客行为,可以克服另一种黑客行为,这是克服反射引擎中的限制所必需的。感谢您对此进行研究!今晚我将尝试你的解决方案。我设法用你的反思建议解压了全班!谢谢你的建议。
gma.getClass().getClassLoader().findClass(gma.getClass().getName());
class DelegateExtractor extends ClassFileLocator.AgentBased {

  private final ClassLoader classLoader;
  private final Instrumentation instrumentation;

  public DelegateExtractor(ClassLoader classLoader, Instrumentation instrumentation) {
    super(classLoader, instrumentation);
    this.classLoader = classLoader;
    this.instrumentation = instrumentation;
  }

  @Override
  public Resolution locate(String typeName) {
    try {
      ExtractionClassFileTransformer classFileTransformer = 
          new ExtractionClassFileTransformer(classLoader, typeName);
      try {
        instrumentation.addTransformer(classFileTransformer, true);
        // Start nasty hack
        Field field = ClassLoader.class.getDeclaredField("classes");
        field.setAccessible(true);
        instrumentation.retransformClasses(
            (Class<?>) ((Vector<?>) field.get(classLoader)).get(0));
        // End nasty hack
        byte[] binaryRepresentation = classFileTransformer.getBinaryRepresentation();
        return binaryRepresentation == null
          ? Resolution.Illegal.INSTANCE
          : new Resolution.Explicit(binaryRepresentation);
      } finally {
        instrumentation.removeTransformer(classFileTransformer);
      }
    } catch (Exception ignored) {
      return Resolution.Illegal.INSTANCE;
    }
  }
}