如何查看Bytecodespring框架生成的proxy类?

How to view Bytecode spring framework generated proxy classes?

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

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);
}

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

private static void printClassBytes(Object obj) throws Exception{

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

无论如何,一件简单的事情就是实现一个 Java 代理并将其附加到 Java 命令行,通过 -javaagent:/path/to/my-agent.jar 启动 Spring 项目。我为您找到了 this article,它解释了如何使用清单文件等实现一个简单的 Java 代理,以及如何通过 Java attach [= 将其附加到 运行 进程49=]。本文以Javassist为例写了一个transformer,不过你也可以直接用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的其余部分,只包含代理工具。

这会将您的 class 简化为(您可以保留或删除 premainagentmain 方法,具体取决于您是否打算将 class 用作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 时,您会得到如下控制台日志:

// 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

(...)