ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

CodeGuide 系列解读:ASM 字节码库引言——动机、双 API 模型与包架构全解析

CodeGuide 系列解读:ASM 字节码库引言——动机、双 API 模型与包架构全解析 文档教程后端【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址https://gitcode.com/gh_mirrors/code/CodeGuide点击查看免费下载导读本文基于开源仓库 CodeGuide 中的《ASM 文档·引言》展开系统讲解 ASM 字节码操控框架诞生的动机、适用范围、双 API 模型基于事件的 API 与基于对象的树 API以及五个 jar 包的模块划分并结合仓库内的 ASM 实战文章帮助你建立从为什么需要字节码技术到如何用它做类生成、类转换与非入侵式监控的完整认知框架。读完本文你将掌握 ASM 的核心设计理念、核心组件分工以及它在 JavaAgent 探针、方法耗时监控等场景中的落地方式。一、动机程序分析、生成与转换为什么重要程序分析、程序生成和程序转换都是非常有用的技术可在许多应用环境下使用程序分析既可能只是简单的语法分析syntaxic parsing也可能是完整的语义分析semantic analysis可用于查找应用程序中的潜在 bug、检测未被用到的代码、对代码实施逆向工程等等。程序生成在编译器中使用。这些编译器不仅包括传统编译器还包括用于分布式程序设计的 stub 编译器或 skeleton 编译器以及 JIT即时编译器等等。程序转换可用于优化或混淆obfuscate程序、向应用程序中插入调试或性能监视代码用于面向方面的程序设计等等。所有这些技术都可针对任意程序设计语言使用但对于不同语言其使用的难易程度可能会有所不同。对于 Java 语言它们可用于 Java 源代码或编译后的 Java 类。为什么选择操作编译后的类在使用经过编译的类时其好处之一显然就是不需要源代码。因此程序转换可用于任何应用程序既包括保密的源代码也包含商业应用程序。使用已编译类的另一个好处是有可能在运行时在马上就要将类加载到 Java 虚拟机之前对类进行分析、生成或转换在运行时生成和编译源代码也可以但其速度很慢而且需要一个完整的 Java 编译器。其好处是诸如 stub 编译器或方面编织器等工具对用户变得透明——用户根本感知不到自己的类被动过手脚而增强的逻辑已经悄然生效。这一理念在仓库的 ASM 实战文章中体现得淋漓尽致。例如 ASM字节码编程 | 如果你只写CRUD那这种技术你永远碰不到 中提到的 ASM 典型用途类的代理如 cglib混沌工程反向工程结合javaagent做到非入侵式监控方法耗时、日志、机器性能等破解。ASM 库的定位与设计目标由于程序分析、生成和转换技术的用途众多人们针对许多语言实现了许多工具ASM 就是为 Java 语言设计的工具之一用于进行运行时也是脱机的类生成与转换。ASM 库被设计用来处理经过编译的 Java 类其设计原则是尽可能保持快速和小型化对于在运行时使用 ASM 进行动态类生成或转换的应用程序来说尽可能提高库的运行速度非常重要这样可以保证这些应用程序的速度不致下降过多保持 ASM 库的小型化一方面是为了在内存有限的环境中使用另一方面也为了避免使那些使用 ASM 的小型应用程序或库增大过多。注ASM 的名字没有任何含义它只是引用 C 语言中的asm关键字——这个关键字允许执行一些用汇编语言编写的函数。ASM 并不是唯一可生成和转换已编译 Java 类的工具但它是最新、最高效的工具之一。其主要优点如下有一个简单的模块 API设计完善、使用方便文档齐全拥有一个相关的 Eclipse 插件注这是《ASM4 使用手册》引言中的历史表述仓库中的 JVM-指令表 等文档即是对该手册的中文整理支持当时的 Java 最新版本原文档以 Java 7 为例ASM 后续版本已持续跟进新版本 JVM小而快、非常可靠拥有庞大的用户社区可以为新用户提供支持源许可开放几乎允许任意使用。二、ASM 的范围与定位ASM 库的目的是生成、转换和分析以字节数组表示的已编译 Java 类它们在磁盘中的存储和在 Java 虚拟机中的加载都采用这种字节数组形式。为此ASM 提供了一些工具使用高于字节级别的概念来读写和转换这种字节数组这些概念包括数值常数、字符串、Java 标识符、Java 类型、Java 类结构元素等等。需要特别注意的是ASM 库的范围严格限制于类的读、写、转换和分析。具体来说类的加载过程就超出了它的范围之外。也就是说ASM 只负责把.class字节码翻译成结构化的事件或对象、以及反向生成字节码至于类如何被 JVM 加载、链接、初始化那是类加载器ClassLoader与 JVM 的事。前置知识一个已编译类里到底有什么要理解 ASM 的范围与模型有必要先了解已编译类的整体结构。仓库文档 2.1 结构 给出了清晰的说明与原生编译应用程序不同已编译类中保留了来自源代码的结构信息和几乎所有符号它包含以下部分专门一部分描述类的修饰符比如public和private、名字、超类、接口和注释类中声明的每个字段各有一部分描述字段的修饰符、名字、类型和注释类中声明的每个方法及构造器各有一部分描述方法的修饰符、名字、返回类型与参数类型、注释并以 Java 字节码指令的形式包含该方法的已编译代码。已编译类与源文件类之间还存在一些关键差异一个已编译类仅描述一个类而一个源文件中可以包含几个类内部类会被编译为独立的类文件已编译类中不包含注释comment但可以包含类、字段、方法和代码属性已编译类中不包含package和import部分因此所有类型名字都必须是完全限定的已编译类中包含常量池constant pool这是一个数组存放类中出现的所有数值、字符串和类型常量仅定义一次之后通过索引引用。幸好 ASM 隐藏了与常量池有关的所有细节所以我们不用再为它操心。三、双 API 模型基于事件的核心 API 与基于对象的树 APIASM 库提供了两个用于生成和转换已编译类的 API核心 API基于事件以基于事件的形式来表示类树 API基于对象以基于对象的形式来表示类。3.1 基于事件的模型在采用基于事件的模型时类是用一系列事件来表示的每个事件表示类的一个元素比如它的一个标头、一个字段、一个方法声明、一条指令等等。基于事件的 API 定义了一组可能事件以及这些事件必须遵循的发生顺序还提供了一个类分析器为每个被分析元素生成一个事件一个类写入器由这些事件的序列生成经过编译的类。3.2 基于对象的模型在采用基于对象的模型时类用一个对象树表示每个对象表示类的一部分比如类本身、一个字段、一个方法、一条指令等等每个对象都有一些引用指向表示其组成部分的对象。基于对象的 API 提供了一种方法可以将表示一个类的事件序列转换为表示同一个类的对象树也可以反过来将对象树表示为等价的事件序列。换言之基于对象的 API 构建在基于事件的 API 之上。仓库文档 6.1 接口和组件 给出了树 API 的核心类ClassNode它的公共字段直接对应类文件的结构部分public class ClassNode ... { public int version; public int access; public String name; public String signature; public String superName; public ListString interfaces; public String sourceFile; public String sourceDebug; public String outerClass; public String outerMethod; public String outerMethodDesc; public ListAnnotationNode visibleAnnotations; public ListAnnotationNode invisibleAnnotations; public ListAttribute attrs; public ListInnerClassNode innerClasses; public ListFieldNode fields; ... }其中name是内部名signature是类签名FieldNode、MethodNode等子节点同样以公共字段 对应类文件子结构的方式组织例如public class FieldNode ... { public int access; public String name; public String desc; public String signature; public Object value; ... }3.3 用 SAX 与 DOM 类比理解两个 API这两个 API 可以与 XML 处理的两套经典 API 进行类比基于事件的 API 类似于SAXSimple API for XML基于对象的 API 类似于DOMDocument Object Model基于对象的 API 构建在基于事件的 API 之上类似于 DOM 可以在 SAX 的上层提供。3.4 为什么同时提供两个 APIASM 之所以要提供两个 API是因为没有哪种 API 是最佳的每个 API 都有自己的优缺点基于事件的 API 更快、内存占用更少因为它不需要在内存中创建和存储用于表示类的对象树SAX 与 DOM 之间也有同样的差异但使用基于事件的 API 时类转换的实现可能要更难一些因为在任意给定时刻类中只有一个元素可供使用也就是与当前事件对应的元素而使用基于对象的 API 时可以在内存中获得整个类操作起来更自由。需要注意这两个 API 都只能同时维护一个类而且独立于其他类。也就是说它们不会维护有关类层级结构的信息如果类的转换影响到其他类那其他这些类的修改应当由用户负责完成。四、体系结构事件链与转换链ASM 应用程序拥有一个很强的体系结构方面。对于基于事件的 API其组织结构是围绕以下三类角色进行的事件生成器类分析器事件使用器类写入器各种预定义的事件筛选器类访问器适配器。在这一结构中可以添加用户定义的生成器、使用器和筛选器。因此这一 API 的使用分为两个步骤将事件生成器、筛选器和使用器组件组装为可能很复杂的体系结构然后启动事件生成器以执行生成或转换过程。基于对象的 API 也有一个体系结构方面用于操作类树的类生成器或转换器组件是可以组合的它们之间的链接代表着转换的顺序。典型的体系结构是一条读取 → 筛选 → 写出的链条ClassReader产生事件 → 若干ClassVisitor筛选/转换事件 →ClassWriter消费事件并输出字节码在整个链中的任何位置都可能会在基于事件与基于对象的表示之间进行转换。4.1 三个核心组件仓库文档 2.2 接口和组件 指出ASM 提供了三个基于ClassVisitorAPI 的核心组件ClassReader类分析器。它分析以字节数组形式给出的已编译类并针对在其accept方法参数中传送的ClassVisitor实例调用相应的visitXxx方法。这个类可以看作一个事件产生器ClassWriterClassVisitor抽象类的一个子类它直接以二进制形式生成编译后的类生成一个字节数组形式的输出可用toByteArray()方法提取。这个类可以看作一个事件使用器ClassVisitor事件筛选器。它将收到的所有方法调用都委托给另一个ClassVisitor类。ClassVisitor抽象类中的每个方法都对应于同名的类文件结构部分其中visitAnnotation、visitField和visitMethod方法分别返回辅助的AnnotationVisitor、FieldVisitor和MethodVisitorpublic abstract class ClassVisitor { public ClassVisitor(int api); public ClassVisitor(int api, ClassVisitor cv); public void visit(int version, int access, String name, String signature, String superName, String[] interfaces); public void visitSource(String source, String debug); public void visitOuterClass(String owner, String name, String desc); AnnotationVisitor visitAnnotation(String desc, boolean visible); public void visitAttribute(Attribute attr); public void visitInnerClass(String name, String outerName, String innerName, int access); public FieldVisitor visitField(int access, String name, String desc, String signature, Object value); public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions); void visitEnd(); }ClassVisitor类的方法必须按以下顺序调用这是 Javadoc 中规定的约束visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )* ( visitInnerClass | visitField | visitMethod )* visitEnd也就是说必须首先调用visit然后是对visitSource的最多一个调用接下来是对visitOuterClass的最多一个调用然后是可按任意顺序对visitAnnotation和visitAttribute的任意多个访问接下来是可按任意顺序对visitInnerClass、visitField和visitMethod的任意多个调用最后以一个visitEnd调用结束。4.2 转换类筛选器的三种玩法将ClassReader与ClassWriter通过一个自定义的ClassVisitor连接起来就构成了最基础的类转换能力修改类成员通过改变visitField、visitMethod方法的access或name参数可以改变字段或方法的修饰符或名字通过改变visit方法的版本号等参数可以实现版本迁移等转换。移除类成员选择不转发某些调用即可移除对应元素。例如RemoveDebugAdapter通过让visitSource、visitOuterClass、visitInnerClass方法体为空来移除调试信息对于字段和方法由于visitField/visitMethod必须返回结果移除时需返回null。RemoveMethodAdapter即按名字 描述符双重匹配仅用名字不足以标识一个方法因为一个类中可能包含若干个具有不同参数的同名方法来移除指定方法。增加类成员发出比收到更多的调用即可增加成员。但新调用的插入位置必须遵守visitXxx的调用顺序例如添加字段的调用只能放在visitInnerClass、visitField、visitMethod或visitEnd方法中。AddFieldAdapter的实践是在visitField中检测目标字段是否已存在在visitEnd中完成字段的追加并注意对fv ! null的判空。此外转换链不一定是线性的可以编写一个MultiClassAdapter将接收到的所有方法调用同时转发给几个ClassVisitor反过来几个类适配器也可以委托至同一个ClassVisitor此时需采取预防措施确保visit和visitEnd恰好只被调用一次。4.3 在 JavaAgent 中落地从 ClassFileTransformer 到 premain引言中指出基于事件的 API 在运行时非常有用。仓库文档 2.2 接口和组件 演示了如何在java.lang.instrument的ClassFileTransformer内部完成全类转换public static void premain(String agentArgs, Instrumentation inst) { inst.addTransformer(new ClassFileTransformer() { public byte[] transform(ClassLoader l, String name, Class c, ProtectionDomain d, byte[] b) throws IllegalClassFormatException { ClassReader cr new ClassReader(b); ClassWriter cw new ClassWriter(cr, 0); ClassVisitor cv new ChangeVersionAdapter(cw); cr.accept(cv, 0); return cw.toByteArray(); } }); }在仓库的实战文章 JavaAgentASM字节码插桩采集方法名称以及入参和出参结果并记录方法耗时 中这一模式被完整落地PreMain.premain通过inst.addTransformer(new ProfilingTransformer())注册转换器ProfilingTransformer.transform方法内用ClassReader读取类字节码、ClassWriterCOMPUTE_MAXS模式写出、ProfilingClassAdapter作为筛选器、cr.accept(cv, ClassReader.EXPAND_FRAMES)驱动整个事件链最终返回增强后的字节码数组。这就是在类加载进 JVM 之前悄悄改造方法的标准姿势。五、组织形式五个 jar 包与文档结构ASM 库划分为几个包以几个 jar 文件的形式进行分发包所在 jar职责org.objectweb.asm和org.objectweb.asm.signatureasm.jar定义基于事件的 API并提供类分析器和写入器组件org.objectweb.asm.utilasm-util.jar提供各种基于核心 API 的工具用于开发和调试 ASM 应用程序org.objectweb.asm.commonsasm-commons.jar提供几个很有用的预定义类转换器大多基于核心 APIorg.objectweb.asm.treeasm-tree.jar定义基于对象的 API并提供工具在事件表示与对象表示之间转换org.objectweb.asm.tree.analysisasm-analysis.jar提供类分析框架和几个预定义类分析器以树 API 为基础5.1 辅助工具一览仓库文档 2.3 工具 介绍了asm-util中几个极为实用的开发调试工具Type用于在内部名/描述符与源代码形式之间进行手动转换。例如Type.getType(String.class).getInternalName()得到java/lang/StringType.getArgumentTypes((I)V)返回包含Type.INT_TYPE的数组Type.getReturnType((I)V)返回Type.VOID_TYPE。TraceClassVisitor打印所访问类的文本表示可同时委托给ClassWriter用于在生成链或转换链的任意位置查看当前类长什么样还能用String.equals()对比两个类。CheckClassAdapter不打印文本而是校验方法调用顺序与参数有效性出错时抛出IllegalStateException或IllegalArgumentException把 JVM 验证器可能拒绝的错误尽量提前暴露。ASMifier为TraceClassVisitor提供替代后端把每次访问调用打印为对应的 Java 代码——也就是说用 ASMifier 访问一个已编译类就能得到用 ASM 生成这个类的源码。它也支持命令行使用例如java -classpath asm.jar:asm-util.jar org.objectweb.asm.util.ASMifier java.lang.Runnable5.2 文档的两大部分《ASM 文档》共分为两大部分仓库中的docs/md/bytecode/asm-document目录完整收录了这份手册第一部分核心 API即asm、asm-util和asm-commons存档文件对应 第 2 章·类含 2.1 结构、2.2 接口和组件、2.3 工具、第 3 章·方法、第 4 章·元数据 等第二部分树 API即asm-tree和asm-analysis存档文件对应 第 6 章·类树 API 等章节。每部分至少包含该 API 与类相关的一章、与方法相关的一章、与注释和泛型等相关的一章每章都会介绍编程接口及相关的工具与预定义组件。这种组织形式便于循序渐进地介绍类文件特征但有时需要将同一个 ASM 类的介绍分散到几节中因此文档建议依次阅读如需查阅 API 参考手册则使用 Javadoc。原文档还附带了印刷约定斜体用于强调句子中的元素中文版一般改为加粗显示定宽字体用于表示代码段粗体定宽字体用于强调代码元素斜体定宽字体用于表示标记和代码中的变量部分。六、从引言到实战仓库中的 ASM 应用佐证引言讲清楚了为什么与是什么仓库的 ASM 实战系列则给出了怎么用。以下案例均来自docs/md/bytecode/asm目录可以作为阅读引言后的第一步实践。6.1 描述符是进入字节码世界的钥匙无论使用哪个 API都必须先掌握两个核心概念详见 2.1 结构类型描述符字段类型在已编译类中的表示Java 类型类型描述符booleanZcharCbyteBshortSintIfloatFlongJdoubleDObjectLjava/lang/Object;int[][IObject[][][[Ljava/lang/Object;方法描述符以左括号开头、后跟各形参类型描述符、右括号、返回类型描述符返回 void 用 V源文件中的方法声明方法描述符void m(int i, float f)(IF)Vint m(Object o)(Ljava/lang/Object;)Iint[] m(int i, String s)(ILjava/lang/String;)[IObject m(int[] i)([I)Ljava/lang/Object;例如([Ljava/lang/String;)V就等价于void main(String[] args)。6.2 用 ClassWriter 从零生成一个 HelloWorldASM字节码编程 | 如果你只写CRUD那这种技术你永远碰不到 演示了用ClassWriter直接生成字节码类——这正是引言中程序生成的实例。普通的 HelloWorld 经javap -c反汇编后就是getstatic、ldc、invokevirtual、return四条指令反过来用 ASM 的MethodVisitor逐条写出这四条指令即可import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; private static byte[] generate() { ClassWriter classWriter new ClassWriter(0); // 定义对象头版本号、修饰符、全类名、签名、父类、实现的接口 classWriter.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, org/itstack/demo/asm/AsmHelloWorld, null, java/lang/Object, null); // 添加方法修饰符、方法名、描述符、签名、异常 MethodVisitor methodVisitor classWriter.visitMethod(Opcodes.ACC_PUBLIC Opcodes.ACC_STATIC, main, ([Ljava/lang/String;)V, null, null); // 执行指令获取静态属性 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, java/lang/System, out, Ljava/io/PrintStream;); // 加载常量 load constant methodVisitor.visitLdcInsn(Hello World); // 调用方法 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, java/io/PrintStream, println, (Ljava/lang/String;)V, false); // 返回 methodVisitor.visitInsn(Opcodes.RETURN); // 设置操作数栈的深度和局部变量的大小 methodVisitor.visitMaxs(2, 1); methodVisitor.visitEnd(); classWriter.visitEnd(); // 生成字节数组 return classWriter.toByteArray(); }其中的关键点visit定义类头相当于public class HelloWorldvisitMethod定义方法需指定修饰符、方法名、方法描述符MethodVisitor上按序执行指令最后必须用visitMaxs设置操作数栈深度与局部变量大小并以visitEnd收尾。生成的字节数组既可以用自定义ClassLoader.defineClass动态加载也可以落盘成.class文件。该文章还推荐借助 IDEA 的 ASM Bytecode Outline 插件辅助开发快速把一段 Java 代码翻译成 ASM 调用序列。6.3 事件模型下的方法级增强耗时监控与入参采集引言强调在运行时对类进行转换的价值实战文章则展示了最典型的方法级增强。通过继承ClassVisitor重写visitMethod再返回一个继承AdviceAdapter的MethodVisitor即可在方法进入/退出时织入监控代码方法进入onMethodEnter插入System.nanoTime()记录开始时间INVOKESTATICLSTORE或使用GETSTATIC/ALOAD/INVOKEVIRTUAL打印入参方法退出onMethodExit对IRETURN~RETURN与ATHROW等退出指令构造StringBuilder计算nanoTime()差值并打印方法耗时。该实战中的输出示例截取自原文运行结果access1 namequeryUserInfo desc(Ljava/lang/String;)Ljava/lang/String; xxxx xxxx xxxx xxxx 方法执行耗时(纳秒)-queryUserInfo132300 测试结果100016.4 JavaAgent ASM非入侵式全链路监控更进一步JavaAgentASM字节码插桩采集方法名称以及入参和出参结果并记录方法耗时 将引言中的运行时转换理念完整落地为一个可上线的监控方案定义测试方法如String queryUserInfo(int uId, int age)监控入口PreMain.premain(String agentArgs, Instrumentation inst)中inst.addTransformer(new ProfilingTransformer())并在MANIFEST.MF中配置Manifest-Version: 1.0 Premain-Class: org.itstack.sqm.asm.PreMain Can-Redefine-Classes: true字节码处理ProfilingTransformer.transform内用ClassReaderClassWriter(COMPUTE_MAXS) 自定义ClassVisitorEXPAND_FRAMES完成增强方法解析利用方法描述符如(II)Ljava/lang/String;通过正则(L.*begin▁of▁sentence#59;|\[{0,2}L.*?;|[ZCBSIFJD]|\[{0,2}[ZCBSIFJD]{1})拆解入参出参类型并对每个方法生成全局唯一的原子自增methodIdAtomicIntegerAtomicReferenceArrayMethodTag避免每次调用都传完整方法信息造成的性能损耗方法进入增强onMethodEnter中织入System.nanoTime()记录开始时间并按参数类型用ILOAD/FLOAD/LLOAD/DLOAD 各包装类valueOf装箱后AASTORE装填Object[]参数数组方法退出增强onMethodExit中按RETURN/ARETURN分支处理出参最终以LLOAD开始时间LDCmethodIdALOAD参数数组/返回值调用预设的静态方法ProfilingAspect.point统一输出。增强后的方法等价于public String queryUserInfo(int var1, int var2) throws InterruptedException { long var3 System.nanoTime(); Object[] var6 new Object[]{var1, var2}; Object var7 你好bugstack虫洞栈 | 精神小伙; ProfilingAspect.point(var3, 0, var6, var7); return var7; }在 IDEA 的 VM options 中配置-javaagent:/path/to/SQM-1.0-SNAPSHOT.jar后即可无侵入地得到类名、方法名、入参类型与值、出参类型与值以及耗时的完整监控输出。这与仓库 README 中登记的ASM字节码编程itstack-demo-asm、SQM系列项目一脉相承。七、总结引言是全套 ASM 文档的总纲它用程序分析、程序生成、程序转换三条主线解释了字节码技术的价值明确了 ASM快速、小型化、专精于已编译类的定位给出了事件模型核心 API与对象模型树 API的二元划分与 SAX/DOM 类比勾勒出事件生成器—筛选器—使用器的体系结构并厘清了五个 jar 包的模块边界。以此为骨架再配合仓库中 第 2 章·类、第 3 章·方法、第 6 章·树 API 等章节以及docs/md/bytecode/asm下的实战文章你就能从懂概念走到能编码用ClassWriter生成类、用ClassVisitor转换类、用JavaAgent实现非入侵式监控。字节码编程并非只属于框架作者它正是突破 CRUD 瓶颈、理解 Spring、MyBatis、CGLIB、各类 APM 探针底层原理的一把钥匙。赞分享文档教程后端【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址https://gitcode.com/gh_mirrors/code/CodeGuide点击查看免费下载相关推荐ASM 泛型签名全解析从 Signature 语法规则到 SignatureVisitor 字节码读写实战ASM 泛型签名全解析从 Signature 语法规则到 SignatureVisitor 字节码读写实战 泛型信息在 class 文件中并不存在于类型/方法文档教程后端CodeGuide 中间件实战非入侵监控设计之 ASM 字节码插桩CodeGuide 中间件实战非入侵监控设计之 ASM 字节码插桩 本文源自小傅哥《SpringBoot 中间件设计和开发》专栏第 16 章。在大型互联网架构文档教程后端FastJSON反序列化性能FieldDeserializer与ASM字节码对比FastJSON反序列化性能FieldDeserializer与ASM字节码对比 1. 引言反序列化性能瓶颈与解决方案 在现代Java应用中JSONJa序列化后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表