ASM的组成部分

## 1. ASM的两个组成部分 从组成结构上来说,ASM分成两部分,一部分为Core API,另一部分为Tree API。 - 其中,Core API包括`asm.jar`、`asm-util.jar`和`asm-commons.jar`; - 其中,Tree API包括`asm-tree.jar`和`asm-analysis.jar`。 ![asmcomponents.png](http://www.icode9.com/i/li/?n=2&i=images/20210618/1624028194528190.png?,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=) 从两者的关系来说,Core API是基础,而Tree API是在Core API的这个基础上构建起来的。 从ASM API演进的历史来讲,先有Core API,后有Tree API。最初,在2002年,Eric Bruneton等发表了一篇文章,即《[ASM: a code manipulation tool to implement adaptable systems](https://lsieun.github.io/assets/pdf/asm-eng.pdf)》。在这篇文章当中,最早提出了ASM的设计思路。当时,ASM只包含13个类文件,Jar包的大小只有21KB。这13个类文件,就是现在所说的Core API的雏形,但当时并没有提出Core API这样的概念。随着时代的变化,人们对于修改Java字节码提出更多的需求。为了满足人们的需求,ASM就需要添加新的类。类的数量变多了,代码的管理也就变得困难起来。为了更好的管理ASM的代码,就将这些类(按照功能的不同)分配到不同的Jar包当中,这样就逐渐衍生出Core API和Tree API的概念。 ## 2. Core API概览 ASM Core API概览,就是对`asm.jar`、`asm-util.jar`和`asm-commons.jar`文件里包含的主要类成员进行介绍。 ### 2.1 asm.jar 在`asm.jar`文件中,一共包含了30多个类,我们会介绍其中10个类。那么,剩下的20多个类,为什么不介绍呢?因为剩下的20多个主要起到“辅助”的作用,它们更多的倾向于是“幕后工作者”;而“登上舞台表演的”则是属于那10个类。 在“第二章”当中,我们会主要介绍从“无”到“有”生成一个新的类,其中会涉及到`ClassVisitor`、`ClassWriter`、`FieldVisitor`、`FieldWriter`、`MethodVisitor`、`MethodWriter`、`Label`和`Opcodes`类。 在“第三章”当中,我们会主要介绍修改“已经存在的类”,使之内容发生改变,其中会涉及到`Cla***eader`和`Type`类。 在这10个类当中,最重要的是三个类,即`Cla***eader`、`ClassVisitor`和`ClassWriter`类。这三个类的关系,可以描述成下图: ![asmcoreclasses.png](http://www.icode9.com/i/li/?n=2&i=images/20210618/1624028333369109.png?,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=) 这三个类的作用,可以简单理解成这样: - `Cla***eader`类,负责读取`.class`文件里的内容,然后拆分成各个不同的部分。 - `ClassVisitor`类,负责对`.class`文件中某一部分里的信息进行修改。 - `ClassWriter`类,负责将各个不同的部分重新组合成一个完整的`.class`文件。 在“第二章”当中,主要围绕着`ClassVisitor`和`ClassWriter`这两个类展开,因为在这个部分,我们是从“无”到“有”生成一个新的类,不需要`Cla***eader`类的参与。 在“第三章”当中,就需要`Cla***eader`、`ClassVisitor`和`ClassWriter`这三个类的共同参与。 ### 2.2 asm-util.jar `asm-util.jar`主要包含的是一些**工具类**。 在下图当中,可以看到`asm-util.jar`里面包含的具体类文件。这些类主要分成两种类型:`Check`开头和`Trace`开头。 - 以`Check`开头的类,主要负责检查(Check)生成的`.class`文件内容是否正确。 - 以`Trace`开头的类,主要负责将`.class`文件的内容打印成文字输出。根据输出的文字信息,可以探索或追踪(Trace)`.class`文件的内部信息。 ![asmutiljarclasses.png](http://www.icode9.com/i/li/?n=2&i=images/20210618/1624028362391858.png?,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=) 在`asm-util.jar`当中,主要介绍`CheckClassAdapter`类和`TraceClassVisitor`类,也会简略的说明一下`Printer`、`ASMifier`和`Textifier`类。 在“第四章”当中,会介绍`asm-util.jar`里的内容。 ### 2.3 asm-commons.jar `asm-commons.jar`主要包含的是一些**常用功能类**。 在下图当中,可以看到`asm-commons.jar`里面包含的具体类文件。 ![asmcommonsjarclasses.png](http://www.icode9.com/i/li/?n=2&i=images/20210618/1624028392439879.png?,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=) 我们会介绍到其中的`AdviceAdapter`、`AnalyzerAdapter`、`Cla***emapper`、`GeneratorAdapter`、`InstructionAdapter`、`LocalVariableSorter`、`SerialVersionUIDAdapter`和`StaticInitMerger`类。 在“第四章”当中,介绍`asm-commons.jar`里的内容。 另外,一个非常容易混淆的问题就是,**asm-util.jar**与**asm-commons.jar**有什么区别呢?在`asm-util.jar`里,它提供的是通用性的功能,没有特别明确的应用场景;而在`asm-commons.jar`里,它提供的功能,都是为解决某一种特定场景中出现的问题而提出的解决思路。 ## 3. 搭建ASM开发环境 - JDK版本:1.8.0_261 - Maven版本:3.8.1 - IDEA:2021.1.2 (Community Edition) ```text $ java -version java version "1.8.0_261" Java(TM) SE Runtime Environment (build 1.8.0_261-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, mixed mode) $ mvn -version Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d) Maven home: D:\Software\apache-maven Java version: 1.8.0_261, vendor: Oracle Corporation, runtime: C:\Program Files\Java\jdk1.8.0_261\jre Default locale: zh_CN, platform encoding: GBK OS name: "windows 7", version: "6.1", arch: "amd64", family: "windows" ``` ### 3.1 修改pom.xml 新建一个maven项目,取名为`asm-maven`,修改其中的`pom.xml`文件,添加ASM的Jar包依赖。打开`pom.xml`文件,并添加如下内容: ```xml UTF-81.8${java.version}${java.version}9.0org.ow2.asmasm${asm.version}org.ow2.asmasm-commons${asm.version}org.ow2.asmasm-util${asm.version}org.ow2.asmasm-tree${asm.version}org.ow2.asmasm-analysis${asm.version}org.apache.maven.pluginsmaven-compiler-plugin3.8.1${java.version} ${java.version}true-g-parameters ``` ### 3.2 使用ASM 这个部分涉及的代码,并不需要记忆和理解,主要是为了让大家对ASM的使用有一个初步的认识,为了验证ASM的开发环境是能够正常使用的。 #### 3.2.1 预期目标 我们的预期目标是,生成一个`HelloWorld`类,它对应的Java代码如下: ```java package sample; public class HelloWorld { @Override public String toString() { return "This is a HelloWorld object."; } } ``` 注意,我们不需要去写这样一个`sample/HelloWorld.java`文件,只是生成的`HelloWorld`类和这里的Java代码是一样的效果。 #### 3.2.2 编码实现 ```java package com.example; import org.objectweb.asm.*; public class HelloWorldDump implements Opcodes { public static byte[] dump() { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "sample/HelloWorld", null, "java/lang/Object", null); { MethodVisitor mv1 = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); mv1.visitCode(); mv1.visitVarInsn(ALOAD, 0); mv1.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); mv1.visitInsn(RETURN); mv1.visitMaxs(1, 1); mv1.visitEnd(); } { MethodVisitor mv2 = cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null); mv2.visitCode(); mv2.visitLdcInsn("This is a HelloWorld object."); mv2.visitInsn(ARETURN); mv2.visitMaxs(1, 1); mv2.visitEnd(); } cw.visitEnd(); return cw.toByteArray(); } } ``` #### 3.2.3 验证结果 ```java package com.example; public class MyClassLoader extends ClassLoader { @Override protected Class<? > findClass(String name) throws ClassNotFoundException { if ("sample.HelloWorld".equals(name)) { byte[] bytes = HelloWorldDump.dump(); Class<? > clazz = defineClass(name, bytes, 0, bytes.length); return clazz; } throw new ClassNotFoundException("Class Not Found: " + name); } } ``` ```java package com.example; public class HelloWorldRun { public static void main(String[] args) throws Exception { MyClassLoader classLoader = new MyClassLoader(); Class<? > clazz = classLoader.loadClass("sample.HelloWorld"); Object instance = clazz.newInstance(); System.out.println(instance); } } ``` 运行之后的输出结果: ```text This is a HelloWorld object. ``` ## 4. 总结 本文主要是对ASM的组成部分进行了介绍,内容总结如下: - 第一点,ASM由Core API和Tree API两个部分组成。 - 第二点,Core API概览,就是对`asm.jar`、`asm-commons.jar`和`asm-util.jar`文件里包含的主要类成员进行介绍。 - 第三点,通过一个简单的示例,能够快速搭建起ASM的开发环境。
上一篇:ASM插桩--多线程运行监测,详解系列文章


下一篇:Java ASM系列:(035)TraceClassVisitor介绍