【问题标题】:How to view Bytecode spring framework generated proxy classes?如何查看字节码spring框架生成的代理类?
【发布时间】:2020-07-08 08:49:05
【问题描述】:

Spring AOP/ASM 库中是否有 API 可以让我们读取 Spring Generated Proxy 类的字节码表示。 在我的测试代码中,我可以访问类文件。

private static void printClassBytes(String classFilePath) throws Exception{
    TraceClassVisitor visitor = new TraceClassVisitor(new PrintWriter(System.out));
    ClassReader reader = new ClassReader(new FileInputStream(new File("/xxx/asm_source/asmtest-0.0.1-SNAPSHOT/com/test/asm/Application.class")));
    
    reader.accept(visitor, 0);
}

但是在我的应用程序中,代理类是在运行时使用 Spring Integration Gateway 生成的,我只有代理对象的对象引用。 Spring 或 ASM 中是否有一些 API 可以让我使用对象引用找到相应代理类的字节码 类似的东西

private static void printClassBytes(Object obj) throws Exception{

【问题讨论】:

  • 你的问题太笼统了。请通过更详细地解释您想要做什么并显示源代码来改进它,以便社区可以看到您尝试了什么以及您遇到了什么困难。顺便说一句,我必须下载 Spring ASM 模块才能找出它是什么:只是旧 ASM 版本(2013 年的类文件)的重新打包和重新定位(不同的包名称)变体。所以也许你也应该标记问题asm。我在这里看不到与 spring-aspects 的任何联系。
  • 我有代理类。我在现有 .class 文件上使用的所有示例都使用了访问者。对于 spring 生成的代理,是否有一些实用程序可以帮助我打印字节码
  • 我对编辑问题和提供源代码的请求有什么不清楚的地方?理想情况下,重现您的问题的MCVE 会很好。还请解释您需要字节码的用途。您的问题似乎是 XY problem 的一个实例,询问 如何 在技术上做某事而不是解释 what 您的目标是,从而关闭替代的大门(可能更好)的解决方案。有很多方法可以获取类的字节码,但我想了解您为什么认为需要它。
  • 我问是因为为了编写类转换器并通过 Java 工具应用它,您不需要字节码。 Java 检测基础设施将负责将字节码提供给您的转换器。所以真的,我需要看看你的代码才能看到更多。否则我们就在这里完成了,我害怕。 P.S.:我刚刚注意到您更改了整个问题及其标签,不再谈论 ASM 而是 CGLIB。什么鬼?
  • 抱歉不清楚。我有一个基于 Spring Integration 的应用程序,它在运行时生成代理对象。我有兴趣获取这些代理对象的字节码,以了解 Spring Integration Gateway 的工作原理。现在,我只想打印它们以使用 TraceClassVisitor 进行分析。我是这个主题的新手......如果我又错过了什么,请原谅我。

标签: spring spring-aop java-bytecode-asm cglib


【解决方案1】:

我不知道 Spring 是否有板载方法可以做到这一点,但 AFAIK 嵌入在 Spring 中的 ASM 类不包括 TraceClassVisitor。因此,如果您喜欢它的输出,则必须直接使用 ASM(工件asm-util),我假设您为示例代码所做的。如果您想坚持使用板载方式,您可以编写一个转换器,将完整的字节码作为类文件转储到一个文件中,然后使用 JDK 命令行工具javap 查看该文件,例如通过javap -c -p -v MyDumpedBytes.class

无论如何,一个简单的事情是实现一个 Java 代理并将其附加到 Java 命令行,通过 -javaagent:/path/to/my-agent.jar 启动 Spring 项目。我为您找到了this article,它解释了如何使用清单文件等实现简单的 Java 代理,以及如何通过 Java 附加 API 将其附加到正在运行的进程。本文使用 Javassist 作为编写转换器的示例,但您可以直接使用 ASM。

您的 Java 代理 + 转换器将如下所示:

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.util.TraceClassVisitor;

import java.io.PrintWriter;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

class TraceClassTransformer implements ClassFileTransformer {

  /**
   * Attach agent dynamically after JVM start-up
   */
  public static void agentmain(String commandLineOptions, Instrumentation instr) {
    premain(commandLineOptions, instr);
  }

  /**
   * Start agent via <code>-javaagent:/path/to/my-agent.jar=<i>options</i></code> JVM parameter
   */
  public static void premain(String commandLineOptions, Instrumentation instrumentation) {
    TraceClassTransformer transformer = new TraceClassTransformer();
    instrumentation.addTransformer(transformer, true);
  }

  @Override
  public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    dumpClass(classfileBuffer);
    // Do not apply any transformation
    return null;
  }

  private void dumpClass(byte[] classfileBuffer) {
    TraceClassVisitor visitor = new TraceClassVisitor(new PrintWriter(System.out));
    ClassReader reader = new ClassReader(classfileBuffer);
    reader.accept(visitor, 0);
  }

}

只需确保代理的清单通过Can-Retransform-Classes: true 启用重新转换即可。

代理启动后,您只需拨打instrumentation.retransformClasses(proxyInstance.getClass());即可享受日志输出。

为了让这个例子更简单,让我们使用byte-buddy-agent,它包含一个简洁的小工具集,可以在运行时附加转换器,而不需要将它们包装到Java代理中。工件很小,不包含 ByteBuddy 的其余部分,只是代理工具。

这会将您的类简化为(您可以保留或删除 premainagentmain 方法,具体取决于您是否计划将该类用作 Java 代理):

import net.bytebuddy.agent.ByteBuddyAgent;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.util.TraceClassVisitor;

import java.io.Closeable;
import java.io.PrintWriter;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.reflect.Proxy;
import java.security.ProtectionDomain;

class TraceClassTransformer implements ClassFileTransformer {
  public static void main(String[] args) throws UnmodifiableClassException {
    // Easy way to get an Instrumentation instance, so we can directly register our transformer on it
    Instrumentation instrumentation = ByteBuddyAgent.install();
    // I am just creating a Java dynamic proxy for a JRE interface. In your own application,
    // you would just get a reference to a dynamic proxy created by Spring.
    Object proxyInstance = Proxy.newProxyInstance(
      Closeable.class.getClassLoader(),
      new Class<?>[] { Closeable.class },
      (proxy, method, args1) -> null
    );

    // Register + use dummy ClassFileTransformer, then unregister again (optional)
    TraceClassTransformer transformer = new TraceClassTransformer();
    try {
      instrumentation.addTransformer(transformer, true);
      instrumentation.retransformClasses(proxyInstance.getClass());
    }
    finally {
      instrumentation.removeTransformer(transformer);
    }
  }

  @Override
  public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    dumpClass(classfileBuffer);
    // Do not apply any transformation
    return null;
  }

  private void dumpClass(byte[] classfileBuffer) {
    TraceClassVisitor visitor = new TraceClassVisitor(new PrintWriter(System.out));
    ClassReader reader = new ClassReader(classfileBuffer);
    reader.accept(visitor, 0);
  }
}

在运行示例 main 类时,您会得到如下控制台日志:

// class version 58.0 (58)
// access flags 0x11
public final class com/sun/proxy/$Proxy0 extends java/lang/reflect/Proxy implements java/io/Closeable {


  // access flags 0xA
  private static Ljava/lang/reflect/Method; m0

  // access flags 0xA
  private static Ljava/lang/reflect/Method; m1

  // access flags 0xA
  private static Ljava/lang/reflect/Method; m2

  // access flags 0xA
  private static Ljava/lang/reflect/Method; m3

  // access flags 0x1
  public <init>(Ljava/lang/reflect/InvocationHandler;)V
    ALOAD 0
    ALOAD 1
    INVOKESPECIAL java/lang/reflect/Proxy.<init> (Ljava/lang/reflect/InvocationHandler;)V
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 2

(...)

【讨论】:

  • 非常感谢!!!!!!非常感谢您的帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多