如何查看字节码spring框架生成的代理类?

如何查看字节码spring框架生成的代理类?,spring,spring-aop,java-bytecode-asm,cglib,Spring,Spring Aop,Java Bytecode Asm,Cglib,SpringAOP/ASM库中是否有API允许我们读取Spring生成的代理类的字节码表示。 在我的测试代码中,我可以访问类文件 private static void printClassBytes(String classFilePath) throws Exception{ TraceClassVisitor visitor = new TraceClassVisitor(new PrintWriter(System.out)); ClassReader reader =

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

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);
}
但在我的应用程序中,代理类是在运行时使用SpringIntegrationGateway生成的,我只有代理对象的对象引用。Spring或ASM中是否有API允许我使用对象引用查找相应代理类的字节码 差不多

private static void printClassBytes(Object obj) throws Exception{

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

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

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

import org.objectweb.asm.ClassReader;
导入org.objectweb.asm.util.TraceClassVisitor;
导入java.io.PrintWriter;
导入java.lang.instrument.ClassFileTransformer;
导入java.lang.instrument.IllegalClassFormatException;
导入java.lang.instrument.Instrumentation;
导入java.security.ProtectionDomain;
类TraceClassTransformer实现ClassFileTransformer{
/**
*JVM启动后动态附加代理
*/
公共静态void代理main(字符串命令行选项,指令插入器){
premain(命令行选项,仪表);
}
/**
*通过
-javaagent:/path/to/my-agent.jar=options
JVM参数启动代理 */ 公共静态void premain(字符串命令行选项、指令插入){ TraceClassTransformer transformer=新的TraceClassTransformer(); 仪表。添加变压器(变压器,真实); } @凌驾 公共字节[]转换(类加载器加载器、字符串类名、类正在重新定义、ProtectionDomain ProtectionDomain、字节[]类文件缓冲区)引发IllegalClassFormatException{ dumpClass(classfileBuffer); //不要应用任何转换 返回null; } 私有无效转储类(字节[]classfileBuffer){ TraceClassVisitor=新的TraceClassVisitor(新的PrintWriter(System.out)); ClassReader=新的ClassReader(classfileBuffer); reader.accept(访问者,0); } }
只需确保代理的清单通过
可以重新传输类:true
启用重新传输

代理启动后,您只需调用
instrumentation.retransformClasses(proxyInstance.getClass())然后享受日志输出

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

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

导入net.bytebuddy.agent.ByteBuddyAgent;
导入org.objectweb.asm.ClassReader;
导入org.objectweb.asm.util.TraceClassVisitor;
导入java.io.Closeable;
导入java.io.PrintWriter;
导入java.lang.instrument.ClassFileTransformer;
导入java.lang.instrument.IllegalClassFormatException;
导入java.lang.instrument.Instrumentation;
导入java.lang.instrument.UnmodifiableClassException;
导入java.lang.reflect.Proxy;
导入java.security.ProtectionDomain;
类TraceClassTransformer实现ClassFileTransformer{
公共静态void main(字符串[]args)引发不可修改的类异常{
//获取一个Instrumentation实例的简单方法,因此我们可以直接在它上注册我们的transformer
Instrumentation Instrumentation=ByteBuddyAgent.install();
//我只是为JRE接口创建一个Java动态代理。在您自己的应用程序中,
//您只需要获得对Spring创建的动态代理的引用。
对象proxyInstance=Proxy.newProxyInstance(
Closeable.class.getClassLoader(),
新类[]{Closeable.Class},
(代理,方法,args1)->null
);
//注册+使用虚拟ClassFileTransformer,然后再次注销(可选)
TraceClassTransformer transformer=新的TraceClassTransformer();
试一试{
仪表。添加变压器(变压器,真实);
retransformClasses(proxyInstance.getClass());
}
最后{
仪表.拆卸变压器(变压器);
}
}
@凌驾
公共字节[]转换(类加载器加载器、字符串类名、类正在重新定义、ProtectionDomain ProtectionDomain、字节[]类文件缓冲区)引发IllegalClassFormatException{
dumpClass(classfileBuffer);
//不要应用任何转换
返回null;
}
私有无效转储类(字节[]classfileBuffer){
TraceClassVisitor=新的TraceClassVisitor(新的PrintWriter(System.out));
ClassReader=新的ClassReader(classfileBuffer);
reader.accept(访问者,0);
}
}
运行sample
main
类时,您会得到如下控制台日志:

//类版本58.0(58)
//访问标志0x11
公共最终类com/sun/proxy/$Proxy0扩展了java/lang/r