如何查看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 简化为(您可以保留或删除 premain
和 agentmain
方法,具体取决于您是否打算将 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
(...)
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 简化为(您可以保留或删除 premain
和 agentmain
方法,具体取决于您是否打算将 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
(...)