> **程序分析**、**程序生成**和**程序转换**都是非常有用的技术,可在许多应用环境下使用;ASM 用于**运行时**类生成与转换(处理**经过编译**的 Java 类)。
[TOC]
## 1 概述
- **使用范围**:严格限制于类的读、写、转换和分析 (类的加载过程就超出了它的范围之外)
- **API 模型**:ASM 提供了两个用于生成和转换已编译类的 API,一个是 core API,以基于**事件**的形式来表示类,另一个是 tree API,以基于**对象**的形式来表示类
- core API 要快于 tree API,因为不需要在内存中创建和存储`Object Tree`
- 但是使用 core API 时,类转换的实现要更难一些,因为在任意给定时刻,类中只有一个元素可用,tree API 则可以在内存中获取整个类
- **体系结构**:
- 基于事件的 API 围绕事件生成器(类分析器)、事件使用器(类写入器)和各种预定义的事件筛选器进行,可由开发者自定义,分为两个步骤:
- 将事件生成器(ClassReader)、筛选器(ClassVisitor)和使用器(ClassWriter)组装为体系结构
- 启动事件生成器(ClassReader),以执行生成或转换过程
- 基于对象的 API 中,用于操作 Object Tree 的类生成器或转换器组件是可以通过组装而形成的,它们之间的链接代表着转换的顺序
- **组织形式**:
- `org.objectweb.asm` 和 `org.objectweb.asm.signature` 定义了基于事件的 API,并提供了类分析器和写入器组件 (asm.jar)
- `org.objectweb.asm.util` 提供了各种基于 core API 的工具,可以在开发和调试 ASM 应用程序时使用 (asm-util.jar)
- `org.objectweb.asm.commons` 提供了几个很有用的预定义类转换器,大多基于 core API (asm-commons.jar)
- `org.objectweb.asm.tree` 定义了基于对象的 API,并提供了一些工具,用于在基于事件和基于对象的表示方法之间进行转换 (asm-tree.jar)
- `org.objectweb.asm.tree.analysis` 以 tree API 为基础,提供了一个类分析框架和几个预定义的类分析器 (asm-analysis.jar)
## 2 已编译类的结构表示
一个**已编译类**和**源文件类**有以下几点区别:
- 已编译类仅描述**一个类**,一个源文件中可以包含几个类
- 已编译类**不包含注释(comment)**,但可以包含类、字段方法和代码属性(Attribute),Java 5 引入注解(annotaion)后,属性已经变得没有什么用处了
- 已编译类中不包含 **package** 和 **import**,因此所有类型(Type)都是全限定的(Qualified)
- 已编译类中包含 [常量池(constant pool)](http://wiki.baidu.com/pages/viewpage.action?pageId=1285072081),其中包含了在类中出现的所有数值、字符串、和类型常量 (但 ASM 隐藏了与常量池有关的所有细节)
### 2.1 内部名 (InternalName)
类型(Type)只能是类(class)或接口类型(interface),类型在已编译类中用**内部名(InternalName)** 表示;一个类的内部名就是这个类的完全限定名,`.` 号用 `/` 表示,例如 **String** 的内部名为 `java/lang/String`
### 2.2 类型描述符 (Type Descriptor)
内部名只能用于表示类或接口类型,所有其他类型,在已编译类中都是用类型描述符(Type Descriptor)表示,如 `表2.2.1` 所示。
<blockquote id="表2.2.1">表2.2.1 - 类型描述符示例</blockquote>
Java 类型 | 类型描述符 | 补充说明
---- | ----
boolean | Z |
char | C |
byte | B |
short | S |
int | I |
float | F |
long | J |
double | D |
Object | Ljava/lang/Object; | 用 `L**;` 表示一个类,以 `;` 号结尾
int[] | [I | 数组类型的描述符是一个 `[` 号后加上其**组件类型**的描述符
Object[][] | [[Ljava/lang/Obejct; |
### 2.3 方法描述符(Method Descriptor)
方法描述符是一个类型描述符列表,用一个字符串描述一个方法的参数类型(parameter Type)和返回类型(return Type),如 `表2` 所示。
<blockquote id="表2.2.2">表2.2.2 - 方法描述符示例</blockquote>
源文件中的方法声明 | 方法描述符
---- | ----
void m(int i, float f) | (IF)V
int m(Object o) | (Ljava/lang/Object;)I
int[] m (int i, String s) | (ILjava/lang/String;)[I
Object m(int[] arr) | ([I)Ljava/lang/Object;
## 3 与已编译类相关的 ASM API
ASM 提供了三个基于 ClassVisitor API 的核心组件,用于生成和转换类:
- **ClassReader** 可以看作一个事件产生器,可以分析 `byte[]` 形式的已编译类,并调用 accept 方法入参里 ClassVisitor 实例的 visitXxx 方法 (`void ClassReader#accept(ClassVisitor, Attribute[], int)`)
- **ClassWriter** 可以看作一个事件使用器,其直接以二进制形式生成已编译类 (`byte[] ClassWriter#toByteArray()`)
- **ClassVistor** 可以看作一个事件筛选器,可以将它收到的所有**方法调用**都委托给另一个 ClassVisitor 类
### 3.1 ClassVisitor:类的生成与转换
用于生成和转换已编译类的 ASM API 是基于 **ClassVisitor** 抽象类的:
<blockquote id="清单3.1.1">清单3.1.1 - ClassVisitor 源码</blockquote>
```java
public abstract class ClassVisitor {
protected final int api;
protected ClassVisitor cv;
public ClassVisitor(final int api) {...}
public ClassVisitor(final int api, final 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) {...}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {...}
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, 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) {...}
public void visitEnd() {...}
}
```
**ClassVisitor 类的方法必须按以下顺序调用:**
1. 首先调用 visit
2. 然后是对 visitSource 的最多一次调用
3. 接下来是对 visitOuterClass 的最多一次调用
4. 然后可按任意顺序对 visitAnnotation 和 visitAttribute 的任意多次调用
5. 接着可按任意顺序对 visitInnerClass、visitField 或 visitMethod 的任意多次调用
6. 最后是对 visitEnd 的一次调用
<blockquote id="清单3.1.2">清单3.1.2 - ClassVisitor 类方法的访问顺序</blockquote>
```
visit visitSource? visitOuterClass? (visitAnnotation | visitAttribute)* (visitInnerClass | visitField | visitMethod)* visitEnd
```
### 3.2 ClassReader:类的分析
在分析一个已存在的类时,唯一必需的组件是 ClassReader,以下是一个用例。
<blockquote id="清单3.2.1">清单3.2.1 - 一个用于读取 HashMap 类信息的 ClassReader 示例</blockquote>
```java
public class PrinterClassVisitor extends ClassVisitor {
public static void main(String[] args) throws IOException {
PrinterClassVisitor cv = new PrinterClassVisitor();
ClassReader cr = new ClassReader("java.util.HashMap");
cr.accept(cv, 0);
}
public PrinterClassVisitor() {
super(Opcodes.ASM4);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
System.out.println(name + " extends " + superName + " {");
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
System.out.println(" " + desc + " " + name);
return null;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
System.out.println(" " + name + desc);
return null;
}
@Override
public void visitEnd() {
System.out.println("}");
}
}
/* 控制台输出:
java/util/HashMap extends java/util/AbstractMap {
J serialVersionUID
I DEFAULT_INITIAL_CAPACITY
...
hash(Ljava/lang/Object;)I
...
internalWriteEntries(Ljava/io/ObjectOutputStream;)V
}
*/
```
### 3.3 ClassWriter:类的生成
为了生成一个类,唯一必需的组件是 ClassWriter,以下是一个用 ClassWriter 生成 A 接口的例子。
> 清单3.3.1 - `pkg.A` 接口
```java
package pkg;
public interface A extends Runnable {
int field1 = -1;
int field2 = 0;
int method1 = (Object o);
}
```
> 清单3.3.1 - 用于生成接口 `pkg/A` 的 ClassWriter 用例
```java
public class ClassWriterTest {
public static void main(String[] args) {
int jdkVersion = V1_5;
int accessFlags = ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE;
String name = "pkg/A";
String signature = null;
String superName = "java/lang/Object";
String[] interfaces = new String[]{"java/lang/Runnable"};
ClassWriter cw = new ClassWriter(0);
cw.visit(jdkVersion, accessFlags, name, signature, superName, interfaces);
cw.visitField(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, "field1", "I", null, -1).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, "field2", "I", null, 0).visitEnd();
cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "method1", "(Ljava/lang/Object;)I", null, null).visitEnd();
cw.visitEnd();
// 使用生成的类
// cw.toByteArray();
}
}
```
**使用 ClassWriter 生成的类:**
`cw.toByteArray()` 返回的字节数组可以存储到 `A.class` 文件中,也可以通过**类加载器(ClassLoader)**动态加载之。
> 清单3.3.2 - 通过自定义类加载器使用 `ClassWriter` 生成的类
```java
public class ClassWriterTest {
public static void main(String[] args) {
ClassWriter cw = new ClassWriter(0);
// 生成类 ...
// 使用生成的类
byte[] bytes = cw.toByteArray();
AClassLoader cl = new AClassLoader();
Class classA = cl.defineClass("pkg.A", bytes);
// 使用 classA ...
}
private static class AClassLoader extends ClassLoader {
public Class defineClass(String name, byte[] bytes) {
return defineClass(name, bytes, 0, bytes.length);
}
}
}
```
### 3.4 转换类 (transfor)
(1) 将 ClassReader 产生的事件转给 ClassWriter
<blockquote id="清单3.4.1">清单3.4.1</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
byte[] in = readClassByte();
ClassReader cr = new ClassReader(in);
ClassWriter cw = new ClassWriter(0);
cr.accept(cw, 0);
byte[] out = cw.toByteArray();
</pre>
(2) 在 ClassReader 和 ClassWriter 之间引入一个 ClassVisitor
<blockquote id="清单3.4.2">清单3.4.2</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
byte[] in = readClassByte();
ClassReader cr = new ClassReader(in);
ClassWriter cw = new ClassWriter(0);
<b>ClassVisitor cv = newClassVistor(cw);
cr.accept(cv, 0);</b>
byte[] out = cw.toByteArray();
</pre>
(3) 重写 ClassVisitor 的方法,实现相应的功能
<blockquote id="清单3.4.3">清单3.4.3</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
private static ClassVisitor newClassVistor(ClassWriter cw) {
return new AClassVisitor(cw);
}
private static class AClassVisitor extends ClassVisitor {
public AClassVisitor(ClassVisitor cw) {
super(ASM4, cw);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(<b>V1_5</b>, access, name, signature, superName, interfaces); // 可以修改版本号
}
}
</pre>
(4) 上面的代码中,整个 `in` 都被**分析**,并**重新构建**了 `out`,如果**将 in 中不被转换的部分直接拷贝到 out 中,不对其分析,也不生成相应的事件**,效率会高得多;ASM 会自动为方法执行这一优化:
- 在 ClassReader#accept 中传入了 ClassVisitor,如果返回的 MethodVisitor 来自一个 ClassWriter,则整个方法的内容将不会被转换
- 在这种情况下,ClassReader 不会分析这个方法的内容,也不会生成相应的事件,只是复制 ClassWriter 中表示这个方法的字节数组
<blockquote id="清单3.4.4">清单3.4.4</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
byte[] in = readClassByte();
ClassReader cr = new ClassReader(in);
<b>ClassWriter cw = new ClassWriter(cr, 0);</b> //执行这一优化
ClassVisitor cv = newClassVistor(cw);
cr.accept(cv, 0);
byte[] out = cw.toByteArray();
</pre>
### 3.5 移除类成员
在重写 ClassVisitor 的方法时,**不转发相应的调用**,可以**移除**相应的类成员(member)。
<blockquote id="清单3.5.1">清单3.5.1 - 一个移除类字段的例子</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
class RemoveMethodAdapter extends ClassVisitor {
<b>private String mName;
private String mDesc;</b>
public RemoveMethodAdapter(ClassVisitor cv, String mName, String mDesc) {
super(ASM4, cv);
this.mName = mName;
this.mDesc = mDesc;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
<b>if (name.equals(mName) && desc.equals(mDesc)) {
return null; // 不要委托至下一个访问器,这样将移除该方法
}</b>
return cv.visitMethod(access, name, desc, signature, exceptions);
}
}
</pre>
### 3.6 增加类成员
在重写 ClassVisitor 的方法时,**遵循 `2.1` 的规则,多转发一些调用**,可以**增加**相应的类成员(member)。
例如,如果要向类中添加一个字段,必须在原方法调用之间添加对 visitField 的一个新调用,而且必须将这个新调用放在类适配器的一个访问方法中(可以在 **visitEnd** 中添加字段,确保字段名称不会重复),以下是一个在类中添加字段的用例:
<blockquote id="清单3.6.1">清单3.6.1 - 一个增加类字段的例子</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
class AddFieldAdapter extends ClassVisitor {
private int fAcc;
private String fName;
private String fDesc;
private boolean isFieldPresent;
public AddFieldAdapter(ClassVisitor cv, int fAcc, String fName, String fDesc) {
super(ASM4, cv);
this.fAcc = fAcc;
this.fName = fName;
this.fDesc = fDesc;
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
<b>if (name.equals(fName)) {
isFieldPresent = true; // 确保字段不会重复
}</b>
return cv.visitField(access, name, desc, signature, value);
}
@Override
public void visitEnd() {
<b>if (!isFieldPresent) {
FieldVisitor fv = cv.visitField(fAcc, fName, fDesc, null, null);
if (fv != null) { // 一个类访问器可以在 visitEnd 中返回 null
fv.visitEnd();
}
}</b>
cv.visitEnd();
}
}
</pre>
### 3.7 转换链
将几个适配器链接在一起,可以组成几个独立的类转换,以完成复杂转换。
<blockquote id="清单3.7.1">清单3.7.1 - 通过编写一个 ClassVisitor 将接收到的方法调用同时转发给几个 ClassVisitor</blockquote>
<pre style="font-family: 'Courier New','MONACO'">
class MultiClassAdapter extends ClassVisitor {
<b>protected ClassVisitor[] cvs;</b>
public MultiClassAdapter(ClassVisitor[] cvs) {
super(ASM4);
this.cvs = cvs;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
<b>for (ClassVisitor cv : cvs) {
cv.visit(version, access, name, signature, superName, interfaces);
}</b>
}
}
</pre>
> 清单3.7.2 - 一个相对复杂的转换链示意图
<pre style="font-family: 'Courier New','MONACO'">
CR → [ ] [ ] → [ ]
↘ ↗ ↘
[ ] → [ ] → [ ]
CR → [ ] ↗ ↘ ↘
↘ CW
↘ ↗
CR → [ ] → [ ]
</pre>
## 4 已编译方法的结构表示
> 在已编译类的内部,方法的代码存储为一系列**字节码**指令,为了生成和转换类,最根本的办法就是**要了解这些指令,并理解它们是如何工作的**
### 4.1 执行模式
- Java 代码在**线程**内部执行
- 每个线程都有自己的执行栈,栈由**帧**组成
- 每个帧表示一个**方法调用**,每一帧包括一个**局部变量**部分和一个**操作数栈**部分
- 局部变量部分与操作数栈部分的大小取决于方法的代码,在**编译时计算完成**,并随字节代码指令一起存储在已编译类中
以下是一个具有 3 帧的执行栈。第 1 帧包含 3 个局部变量,其操作数栈的最大值为 4,其中包含 2 个值;第 2 帧包含 2 个局部变量,操作数栈中有 2 个值;第 3 帧位于执行栈的顶端,包含 4 个局部变量和 2 个操作数。
> 清单4.1.1 - 一个具有 3 帧的执行栈
<pre style="font-family: 'Courier New','MONACO'">
—————————————————— ——————————————— ——————————————————————
| Frame 1 | | Frame 2 | | Frame 3 |
| [L0] [L1] [L2] | | [L0] [L1] | | [L0] [L1] [L2][L3] |
| [V0][V2][][] | | [V0][V2][] | | [V0][V2] |
—————————————————— ——————————————— ——————————————————————
</pre>
- 非静态(static)方法需要保存 this 引用。对于非静态方法,在创建一个帧时,会对其初始化提供一个空栈,并用目标对象 `this` 及该方法的参数来初始化其局部变量。例如调用 `a.equals(b)`时,将创建 1 帧,前 2 个局部变量将被初始化为 a 和 b
- long 和 double 需要两个变量槽。局部变量部分和操作数栈部分的每个**槽(slot)**可以保存 **long 和 double 变量之外的任意 Java 值**。
### 4.2 字节码指令
**字节码的构成:**
字节码指令由标识该指令的**操作码**和固定数目的**参数**组成。
- 操作码是一个 unsigned byte
- 参数是静态值,确定了精确的指令行为,紧跟操作码之后
**字节码的分类:**
字节码可以分为两类,一类用于在局部变量和操作数栈之间传送值,一类仅用于操作数栈。
**用于在局部变量和操作数栈之间传送值的字节码:**
- 读取一个局部变量,并将其值压到操作树栈中,其参数是局部变量的索引 i(必须读取):iload, lload, fload, dload, aload
- 从操作数栈中弹出一个值,并将其值存储在由索引 i 指定的局部变量中:istore, lstore,fstore, dstore, astore
> 注:将一个值存储在局部变量中,然后再以不同类型加载之,是非法的;但是如果向局部变量中存储值,而该值不同于该局部变量中存储的当前值,却是合法的。例如 `istore 1 aload 1` 序列是非法的。
**仅用于操作数栈的字节码:**
- 用于处理 **栈** 上的值:`pop` 弹出栈顶部的值;`dup` 压入顶部栈值的一个副本;`swap` 弹出两个值,并按逆序压入之;……
- 在操作数栈压入一个 **常量** 值:`aconst_null` 压入 null;`iconst_0` 压入 int 值 0;`fconst_0` 压入 0f,`dconst_0` 压入 0d,`bipush B` 压入 byte 值 B;`sipush S` 压入 short 值 S;`ldc CST` 压入任意 int、float、long、double、String 或 class 常量 CST;……
- 从操作数栈弹出数值,进行**算术逻辑**处理后,将结果压入栈中:`*add`、`*sub`、`*mul`、`*div`、`*rem` 分别对应于 `+`、`-`、`*`、`/`、`%` 运算,其中 `*` 表示 `i`、`l`、`f` 或 `d`;还有 `<<`、`>>`、`>>>`、`|`、`&`、`^` 运算的对应指令,用于处理 int 和 long 值
- 从栈中弹出一个值,进行**类型转换**后,将结果压入栈中:`i2f`、`f2d`、`l2d` 等,将数值由一种类型转换为另一种类型;`checkcast T` 将一个引用值转换为类型 T
- 用于创建**对象**、锁定对象、检测对象类型等:例如 `new TYPE` 将一个 TYPE 类型的新对象压入栈中(TYPE 是一个内部名)
- 用于读写一个**字段**的值:`getfield OWNER NAME DESC` 弹出一个对象引用,并压入其 NAME 字段的值;`putfield OWNER NAME DESC` 弹出一个值和对象引用,并将这个值存储在它的 NAME 字段中;这两种情况中,对象必须为 OWNER 类型,字段必须为 DESC 类型;`getstatic` 和 `putstatic` 是类似指令,用于静态字段(static field)。
- 用于调用一个**方法**或构造方法(其弹出值的个数等于其方法参数个数加1(用于目标对象)),并压回方法调用的结果:`invokevirtual OWNER NAME DESC` 调用在类 OWNER 中定义的 NAME 方法,其方法描述为 DESC;`invokestatic OWNER NAME DESC` 用于调用静态方法; `invokespecial` 用于私有方法和构造方法;`invokeinterface` 用于接口中定义的方法;`invokedynamic` 用于动态方法调用机制。
- 用于读写**数组**的值:`Xaload` 弹出一个索引和一个数组,并压入此索引处该数组元素的值;`Xastore` 弹出一个值,一个索引和一个数组,并将这个值存储在数组的这一索引处;这里的 X 可以是 `i`、`l`、`f`、`d`、`a`,`b`、`c`、`s`。
- 用于无条件地或者在某一条件为真时**跳转**到到一条任意指令,用于编译 if、for、do、while、break 和 continue、switch、case 等:例如 `ifeq LABEl` 从栈中弹出一个 int 值,如果该值为 0,则跳转到 LABEL 指定的指令处(否则正常执行下条指令);还有一些跳转指令,诸如 `ifne`、`ifge`、`tableswitch`、`lookupswitch`
### 4.3 字节码指令(执行引擎)工作原理
本节基于以下 User 类,介绍字节码指令的工作原理。
> 清单4.3.1 - 用于演示字节码指令工作原理的 <code id="清单4.3.1">User</code> 类
```java
public class User {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void checkAndSetAge(int age) {
if (age > 0) {
this.age = age;
} else {
throw new IllegalArgumentException();
}
}
}
```
**User#getAge 说明:**
> 清单4.3.2 - `User#getAge` 方法的字节码指令
```java
public int getAge();
Code:
0: aload_0
1: getfield #2 // Field age:I
4: ireturn
```
- 第 1 条指令读取局部变量0,并这个 0 值压入操作数栈
- 第 2 条指令从栈中弹出这个值,即 `this`,并将对象的 `age` 字段压入栈中,即 `this.age`
- 第 3 条指令从栈中弹出这个值,并将其返回给调用者
在这个过程中,执行帧的持续状态如下:
> 清单4.3.3 - `User#getAge` 方法的执行帧的状态
<pre style="font-family: 'Courier New','MONACO'">
(1) 初始状态 (2) aload_0 之后 (3) getfield 之后
[this] [this] [this ]
[ ] [this] [this.age]
</pre>
**User#setAge 说明:**
> 清单4.3.4 - `User#setAge` 方法的字节码指令
```java
public void setAge(int);
Code:
0: aload_0
1: iload_1
2: putfield #2 // Field age:I
5: return
```
- 第 1 条指令将 `this` 压入操作数栈
- 第 2 条指令压入局部变量 1,在为这个**方法调用**创建帧期间,以 age 参数初始化该变量
- 第 3 条指令弹出这两个值,并将 int 值存储在被引用对象的 age 字段中,即存储在 `this.age` 中
- 第 4 条指令表示销毁当前执行帧(编译后强制生成),并返回调用者
> 清单4.3.5 | `User#setAge` 方法的执行帧的状态
<pre style="font-family: 'Courier New','MONACO'">
(1) 初始状态 (2) aload_0 之后 (3) iload_1 之后 (4) putfield 之后
[this][age] [this][age] [this][age] [this][age]
[ ][ ] [this][ ] [this][age] [ ][ ]
</pre>
**User 默认的构造方法说明:**
> 清单4.3.6 - `User#<init>` 方法的字节码指令
```java
public User();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
```
- 第 1 条指令将 `this` 压入操作数栈中
- 第 2 条指令从栈中弹出这个值,并调用 `Object#<init>` 方法
- 第 3 条指令返回调用者
**User#checkAndSetAge 方法说明:**
> 清单4.3.7 - `User#<init>` 方法的字节码指令
```java
public void checkAndSetAge(int);
Code:
0: iload_1
1: ifle 12
4: aload_0
5: iload_1
6: putfield #2 // Field age:I
9: goto 20
12: new #3 // class java/lang/IllegalArgumentException
15: dup
16: invokespecial #4 // Method java/lang/IllegalArgumentException."<init>":()V
19: athrow
20: return
```
- 第 1 条指令将初始化为 age 的局部变量1 压入操作数栈
- 第 2 条指令(ifle)从栈中弹出 1 个值,并将其与 0 进行比较,如果小于等于(le) 0,则跳转到程序计数器(pc)为12的指令,否则不做任何事,继续执行下一条指令
- 第 3 ~ 5 条指令与 `setAge` 方法类似
- 第 6 条指令(goto)表示无条件跳转到 pc 为 20 的指令,即返回
- 第 7 条指令(new)表示创建一个 IllegalArgumentException 实例,并将其压入操作数栈
- 第 8 条指令(dup)表示重复这个值
- 第 9 条指令(invokespecial)表示弹出这两个副本之一,并对其调用构造方法
- 第 10 条指令(athorw)弹出剩下的副本,并将它作为异常抛出(方法异常调用完成),之后不会继续执行下一条指令
**异常表(Exception table):**
不存在用于捕获异常的字节码,而是将一个方法的字节码与一个**异常表(Exception table)**相关联,异常表规定了在某方法中一给定部分抛出异常时必须执行的代码。对于下面的 tryCatchFinallyTest 方法:
> 清单4.3.8 - `tryCatchFinallyTest` 方法,该方法会返回 `300`
```java
public static int tryCatchFinallyTest() {
try {
return 100;
} catch (Exception ex) {
return 200;
} finally {
return 300;
}
}
```
其字节码指令如下:
> 清单4.3.9 - `tryCatchFinallyTest` 方法的字节码指令
```java
public static int tryCatchFinallyTest();
Code:
0: bipush 100
2: istore_0
3: sipush 300
6: ireturn
7: astore_0
8: sipush 200
11: istore_1
12: sipush 300
15: ireturn
16: astore_2
17: sipush 300
20: ireturn
Exception table:
from to target type
0 3 7 Class java/lang/Exception
0 3 16 any
7 12 16 any
}
```
- `from=0, to=3, target=7, type=Exception` 表示:对于 pc 为 0 ~ 3 的指令(try 语句块),如果发生类型为 java/lang/Exception 的异常,将跳转到 pc=7 处,否则执行 pc=3 之后的 `ireturn`
- `from=0, to=3, target=16, type=any` 表示:执行完 pc 为 0 ~ 3 的指令(try 语句块)后,无条件跳转到 pc=16 处的指令(finally 语句块)
- `from=7, to=12, target=16, type=any` 表示:执行完 pc 为 7 ~ 12 的指令(catch 语句块)后,无条件跳转到 pc=16 处的指令(finally 语句块)
## 5 与已编译方法相关的 ASM API
ASM 提供了三个基于 MethodVisitor API 的核心组件,用于生成和转换方法:
- ClassReader 可以看作一个事件产生器,可以分析已编译方法的内容 (void ClassReader#accept(ClassVisitor, Attribute[], int))
- ClassWriter 可以直接以二进制形式生成已编译方法 (MethodVisitor ClassWriter#visitMethod(access, name, desc, signature, exceptions))
- MethodVistor 可以看作一个事件筛选器,将它收到的所有方法调用都委托给另一个 MethodVistor 类
### 5.1 MethodVisitor:方法的生成与转换
用于生成和转换已编译方法的 ASM API 是基于 MethodVisitor 抽象类的:
> 清单5.1.1 - MethodVisitor 源码
```java
public abstract class MethodVisitor {
protected final int api;
protected MethodVisitor mv;
public MethodVisitor(final int api) {}
public MethodVisitor(final int api, final MethodVisitor mv) {}
// Parameters, annotations and non standard attributes
public void visitParameter(String name, int access) {}
public AnnotationVisitor visitAnnotationDefault() {}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {}
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {}
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {}
public void visitAttribute(Attribute attr) {}
public void visitCode() {}
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {}
// Normal instructions
public void visitInsn(int opcode) {}
public void visitIntInsn(int opcode, int operand) {}
public void visitVarInsn(int opcode, int var) {}
public void visitTypeInsn(int opcode, String type) {}
public void visitFieldInsn(int opcode, String owner, String name, String desc) {}
public void visitMethodInsn(int opcode, String owner, String name, String desc) {}
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {}
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {}
public void visitJumpInsn(int opcode, Label label) {}
public void visitLabel(Label label) {}
// Special instructions
public void visitLdcInsn(Object cst) {}
public void visitIincInsn(int var, int increment) {}
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {}
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {}
public void visitMultiANewArrayInsn(String desc, int dims) {}
public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {}
// Exceptions table entries, debug information, max stack and max locals
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {}
public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {}
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {}
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible) {}
public void visitLineNumber(int line, Label start) {}
public void visitMaxs(int maxStack, int maxLocals) {}
public void visitEnd() {}
}
```
**MethodVisitor 类的方法必须按以下顺序调用:**
> 清单5.1.2 - MethodVisitor 类方法的调用顺序
```java
visitAnnotationDefault? (visitAnnotation | visitParameterAnnotation | visitAttribute)* (visitCode (visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn | visitLocalVariable | visitLineNumber)* visitMaxs)? visitEnd
```
- visitCode 和 visitMaxs 方法可用于**检测方法的字节码在一个事件序列中的开始与结束**
- visitEnd 必须在最后调用,用于检测一个方法在一个事件序列中的结束
- 可以将 ClassVisitor 和 MethodVisitor 类合并,生成完整的类
> 清单5.1.3 - 将 ClassVisitor 和 MethodVisitor 类合并,生成完整的类 [书写格式]
<pre style="font-family: 'Courier New','MONACO'">
PrinterClassVisitor cv = new PrinterClassVisitor();
MethodVisitor mv1 = cv.visitMethod(0, "m1", null, null, null);
mv1.visitCode();
mv1.visitInsn(0);
// ...
mv1.visitMaxs(0, 0);
mv1.visitEnd();
MethodVisitor mv2 = cv.visitMethod(0, "m2", null, null, null);
mv2.visitCode();
mv2.visitInsn(0);
// ...
mv2.visitMaxs(0, 0);
mv2.visitEnd();
cv.visitEnd();
</pre>
> 清单5.1.4 - 将 ClassVisitor 和 <code id="清单5.1.4">MethodVisitor</code> 类合并,生成完整的类 [代码示例]
<pre style="font-family: 'Courier New','MONACO'">
/**
* 生成默认的构造方法
*
* @param superName 父类名称
*/
static void generateDefaultConstruct(ClassWriter cw, String superName) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, MethodName.CONSTRUCTOR, MethodDesc.EMPTY_VOID, null, null);
// 生成构造方法的字节码指令
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, superName, MethodName.CONSTRUCTOR, MethodDesc.EMPTY_VOID, false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
/**
* 生成类方法
*/
private static void generateMethod(ClassWriter cw, Class<?> beanClass, boolean usePropertyDescriptor) {
String internalClassName = BeanEnhanceUtils.getInternalName(beanClass.getName());
ClassDesc classDesc = BeanEnhanceUtils.getClassDescription(beanClass, usePropertyDescriptor);
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, MethodName.VALUE, MethodDesc.VALUE, null, null);
mv.visitCode();
// 有属性,需要调用 getter 方法
if (classDesc.hasField) {
generateMethodWithFields(internalClassName, classDesc, mv);
} else {
generateMethodWithNoField(mv, classDesc, internalClassName);
}
mv.visitEnd();
}
/**
* 生成beanClass对应的增强类的字节流
*
* @param superName 父类
* @param interfaces 接口列表
*/
static byte[] generate(Class<?> beanClass, String superName, String[] interfaces, boolean usePropertyDescriptor) throws Exception {
String beanClassName = beanClass.getName();
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);// 自动计算maxStack
String getterClassName = createGeneratedClassName(beanClassName);
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, BeanEnhanceUtils.getInternalName(getterClassName), null, superName, interfaces);
<b>generateDefaultConstruct(cw, superName);</b> // 生成默认构造方法
<b>generateMethod(cw, beanClass, usePropertyDescriptor);</b> // 生成GetterClass
cw.visitEnd();
return cw.toByteArray();
}
</pre>
**new ClassWriter(int flag) 选项:**
- flag 为 0 时,不会进行自动计算,必须自行计算帧,局部变量与操作数栈的大小
- flag 为 `ClassWriter#COMPUTE_MAXS` 时(性能降低10%),ASM 将自动计算局部变量与操作数栈部分的大小,还是必须调用 visitMaxs 方法(可以使用任何参数,但是会被忽略并重新计算),必须自行计算栈帧(实例中 generateDefaultConstruct 方法便是自行计算栈帧)
- flag 为 `ClassWriter#COMPUTE_FRAMES` 时(性能降低50%),一切都是自动计算,不再需要调用 visitFrame,但仍然必须调用 visitMaxs 方法(参数将被忽略并重新计算)
### 5.2 方法的生成
针对以下 User 类(拷贝自 [清单4.3.1](#清单4.3.1)):
> 清单5.2.1 - 用于演示 MethodVisitor 方法生成的 User 类
```java
public class User {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void checkAndSetAge(int age) {
if (age > 0) {
this.age = age;
} else {
throw new IllegalArgumentException();
}
}
}
```
**生成 getAge 方法的 MethodVisitor 用例写法如下:**
> 清单5.2.2 - 用于生成 `User#getAge` 方法的 MethodVisitor 用例
```java
MethodVisitor mv = getMethodVisitor();
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, "pkg/User", "age", "I");
mv.visitInsn(Opcodes.IRETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
```
**生成 checkAndSetAge 方法的 MethodVisitor 用例写法如下:**
> 清单5.2.3 - 用于生成 `User#checkAndSetAge` 方法的 MethodVisitor 用例
<pre style="font-family: 'Courier New','MONACO'">
MethodVisitor mv = getMethodVisitor(); // public void checkAndSetAge(int);
mv.visitCode(); // Code:
mv.visitVarInsn(Opcodes.ILOAD, 1); // 0: iload_1
Label thenBlockLabel = new Label(); // (pc = 12)
mv.visitJumpInsn(Opcodes.IFLE, thenBlockLabel); // 1: ifle 12
mv.visitVarInsn(Opcodes.ALOAD, 0); // 4: aload_0
mv.visitVarInsn(Opcodes.ILOAD, 1); // 5: iload_1
mv.visitFieldInsn(Opcodes.PUTFIELD,
"User", "age", "I"); // 6: putfield #2 // Field age:I
Label elseBlockLabel = new Label();// (pc = 20)
mv.visitJumpInsn(Opcodes.GOTO, elseBlockLabel); // 9: goto 20
mv.visitLabel(thenBlockLabel);
mv.visitFrame(Opcodes.F_SAME,
0, null, 0, null);
mv.visitTypeInsn(Opcodes.NEW,
"java/lang/IllegalArgumentException"); // 12: new #3 // class java/lang/IllegalArgumentException
mv.visitInsn(Opcodes.DUP); // 15: dup
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, // 16: invokespecial #4 // Method java/lang/IllegalArgumentException."<init>":()V
"java/lang/IllegalArgumentException",
"<init>", "()V");
mv.visitInsn(Opcodes.ATHROW); // 19: athrow
mv.visitLabel(elseBlockLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitInsn(Opcodes.RETURN); // 20: return
mv.visitMaxs(2, 2);
mv.visitEnd();
</pre>
## 6 通过 ASM 为模板引擎实现运行时类增强
### 6.1 模板引擎示例
假设存在以下一种使用模板引擎的场景:
1. 开发者创建了 [清单4.3.1](清单4.3.1) 中的 `User` Java 类,如 `清单6.1.1` 所示
2. 开发者在 `user.html` 文件中自定义了数据模板,如 `清单6.1.2` 所示
3. 开发者调用模板引擎,进行数据绑定与结果渲染,如 `清单6.1.3` 所示
4. 模板引擎输出渲染结果,如 `清单6.1.4` 所示
> 清单6.1.1 - 用于演示模板引擎的 User 类
```java
public class User {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void checkAndSetAge(int age) {
if (age > 0) {
this.age = age;
} else {
throw new IllegalArgumentException();
}
}
}
```
> 清单6.1.2 - `user.html` 文件的内容
```html
<pre>
idUserMap 中 key 为 1 所对应的 User 实例的 age = ${idUserMap[1].age}
userList 中下标为 1 所对应的 User 实例的 age = ${userList[1].age}
变量 min_age = ${min_age}
</pre>
<ul><!-- 遍历名称为 "userList" 的列表 -->
<% for (user in userList) { %>
<li>${user.age}</li>
<% } %>
</ul>
<table><!-- 遍历名称为 "idUserMap" 的映射 -->
<% for (entry in idUserMap) {%>
<tr>
<td>${entry.key}</td>
<td>${entry.value.age}</td>
</tr>
<% } %>
</table>
```
> 清单6.1.3 - 通过模板引擎进行数据绑定并渲染结果
```java
Template t = gt.getTemplate("/user.html");
User zhangsan = new User(21);
User lisi = new User(22);
User wangwu = new User(23);
Map<Integer, User> idUserMap = new HashMap<>();
idUserMap.put(1, zhangsan);
idUserMap.put(2, lisi);
List<User> userList = Arrays.asList(zhangsan, lisi, wangwu);
t.binding("zhangsan", zhangsan);
t.binding("idUserMap", idUserMap);
t.binding("userList", userList);
t.binding("min_age", 18);
String res = t.render();
System.out.println(res);
```
> 清单6.1.4 - 模板引擎的数据渲染结果
```html
<pre>
idUserMap 中 key 为 1 所对应的 User 实例的 age = 21
userList 中下标为 1 所对应的 User 实例的 age = 22
变量 min_age = 18
</pre>
<ul><!-- 遍历名称为 "userList" 的列表 -->
<li>21</li>
<li>22</li>
<li>23</li>
</ul>
<table><!-- 遍历名称为 "idUserMap" 的映射 -->
<tr>
<td>1</td>
<td>21</td>
</tr>
<tr>
<td>2</td>
<td>22</td>
</tr>
</table>
```
### 6.2 运行时生成类实例
在 `6.1` 中演示了模板引擎的使用,本文不讨论其语法处理(例如处理循环)的实现,而只是介绍模板引擎如何处理 `User` 类。主要分为以下几个步骤:
1. 将所有支持的类型(List/Map/Array/Model/...)抽象到 `AttributeAccess` 类中(参考`清单6.2.1`),运行时生成 `User$AttributeAccess` 类的 `byte[]`,设为 `code`
2. 运行时通过自定义类加载器加载`code`(参考`清单6.2.2` 与 `清单6.2.3`),定义 `User$AttributeAccess` 类,并返回该类的实例,设为 `user1`
3. 将 `user1` 提供给模板引擎使用
> 清单6.2.1 AttributeAccess 抽象类,封装了一个获取对象的属性的值的方法
```java
public abstract class AttributeAccess implements java.io.Serializable {
public abstract Object value(Object o, Object name);
public void setValue(Object o, Object name, Object value) {
updateValue(o, name, value);
}
// ...
}
```
> 清单6.2.2 自定义类加载器
```java
public class ByteClassLoader extends ClassLoader {
public ByteClassLoader(ClassLoader parent) {
super(parent);
}
public Class<?> defineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
public Class<?> findClassByName(String clazzName) {
try {
return getParent().loadClass(clazzName);
} catch (ClassNotFoundException ignored) {
}
return null;
}
}
```
> 清单6.2.3 通过自定义类加载器加载 code
```java
private Object loadContextClassLoader(byte[] code, String className) {
Object obj;
try {
Class<?> enhanceClass = byteContextLoader.findClassByName(className);
if (enhanceClass == null) {
enhanceClass = byteContextLoader.defineClass(className, code);
}
obj = enhanceClass.newInstance();
} catch (Exception ex) {
return null;
}
return obj;
}
```
综上所述,问题变为: 如何通过 ASM 生成 `User$AttributeAccess` 类
### 6.3 通过 ASM 进行类的生成与转换
运行时,会通过 ASM 生成 AttributeAccess 的子类 `User$AttributeAccess`,参考 [清单5.1.4](#清单5.1.4) 中的 `generate` 方法,ASM 会为 `User$AttributeAccess` 生成以下方法:
- 无参构造方法
- value 方法,对应实现 `AttributeAccess#value(Object, Object)` 方法
> 清单6.3.1 - `generate` 方法
```java
// 生成 AttributeAccess 对应的增强类的字节数组
generate(beanClass, InternalName.ATTRIBUTE_ACCESS, null, usePropertyDescriptor);
/**
* 生成 beanClass 对应的增强类的字节流
*
* @param superName 父类
* @param interfaces 接口列表
*/
static byte[] generate(Class<?> beanClass, String superName, String[] interfaces, boolean usePropertyDescriptor) throws Exception {
String beanClassName = beanClass.getName();
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);// 自动计算maxStack
String getterClassName = createGeneratedClassName(beanClassName);
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, BeanEnhanceUtils.getInternalName(getterClassName), null, superName, interfaces);
// 生成默认构造方法
generateDefaultConstruct(cw, superName);
// 生成类方法
generateMethod(cw, beanClass, usePropertyDescriptor);
cw.visitEnd();
return cw.toByteArray();
}
```
并为其实现 `AttributeAccess#value(Object, Object)` 方法,分三种情况:
- User 中没有 get(String) 或 get(Object) 方法,如 `清单6.3.2` 所示
- User 中包含 get(String) 方法,如 `清单6.3.3` 所示
- User 中包含 get(Object) 方法,如 `清单6.3.4` 所示
> 清单6.3.2 - User 中没有 get(String) 或 get(Object) 方法时,ASM 生成的 value 方法 [示例代码]
<pre style="font-family: 'Courier New','MONACO'">
public Object value(Object bean, Object attr) {
String attrStr = attr.toString();
int hash = attrStr.hashCode();
User user = (User) bean;
switch (hash) {
case 1:
return user.getAge();
case 2:
return user.getXxx();
case 3:
if("age".equals(attrStr)){
return user.getAge();
}
if("xxx".equals(attrStr)){
return user.getXxx();
}
}
<b>throw new RuntimeException(ATTRIBUTE_NOT_FOUND, "attribute : " + attrStr);</b>
}
</pre>
> 清单6.3.3 - User 中包含 get(String) 方法时,ASM 生成的 value 方法 [示例代码]
<pre style="font-family: 'Courier New','MONACO'">
public Object value(Object bean, Object attr) {
String attrStr = attr.toString();
int hash = attrStr.hashCode();
User user = (User) bean;
switch (hash) {
case 1:
return user.getAge();
case 2:
return user.getXxx();
case 3:
if("age".equals(attrStr)){
return user.getAge();
}
if("xxx".equals(attrStr)){
return user.getXxx();
}
}
<b>return user.get(attrStr);</b>
}
</pre>
> 清单6.3.4 - User 中包含 get(Object) 方法时,ASM 生成的 value 方法 [示例代码]
<pre style="font-family: 'Courier New','MONACO'">
public Object value(Object bean, Object attr) {
String attrStr = attr.toString();
int hash = attrStr.hashCode();
User user = (User) bean;
switch (hash) {
case 1:
return user.getAge();
case 2:
return user.getXxx();
case 3:
if("age".equals(attrStr)){
return user.getAge();
}
if("xxx".equals(attrStr)){
return user.getXxx();
}
}
<b>return user.get(attr);</b>
}
</pre>
至此,`User$AttrubiteAccess` 类的生成与转换的过程结束,返回的 `byte[]` 将传给自定义的类加载器。
## 附录
### 附录A 元数据的结构表示
> 表A-1 - 类型签名举例
Java 类型 | 对应的类型签名
---- | ----
`List<E>` | `Ljava/util/List<TE;>;`
`List<?>` | `Ljava/util/List<*>;`
`List<? extends Number>` | `Ljava/util/List<+Ljava/lang/Number;>;`
`List<? super Integer>` | `Ljava/util/List<-Ljava/lang/Integer;>;`
`List<List<String>[]>` | `Ljava/util/List<[Ljava/util/List<Ljava/lang/String;>;>;`
`HashMap<K, V>.HashIterator<K>` | `Ljava/util/HashMap<TK;TV;>.HashIterator<TK;>;`
`static <T> Class<? extends T> m (int n)` | `<T:Ljava/lang/Obejct;>(I)Ljava/lang/Class<+TT>;`
> 表A-2 - 类签名举例
Java 类 | 对应的类签名
---- | ----
`C<E> extends List<E>` | `<E:Ljava/lang/Object;>Ljava/util/List<TE;>;`
- 空白目录
- 精简版Spring的实现
- 0 前言
- 1 注册和获取bean
- 2 抽象工厂实例化bean
- 3 注入bean属性
- 4 通过XML配置beanFactory
- 5 将bean注入到bean
- 6 加入应用程序上下文
- 7 JDK动态代理实现的方法拦截器
- 8 加入切入点和aspectj
- 9 自动创建AOP代理
- Redis原理
- 1 Redis简介与构建
- 1.1 什么是Redis
- 1.2 构建Redis
- 1.3 源码结构
- 2 Redis数据结构与对象
- 2.1 简单动态字符串
- 2.1.1 sds的结构
- 2.1.2 sds与C字符串的区别
- 2.1.3 sds主要操作的API
- 2.2 双向链表
- 2.2.1 adlist的结构
- 2.2.2 adlist和listNode的API
- 2.3 字典
- 2.3.1 字典的结构
- 2.3.2 哈希算法
- 2.3.3 解决键冲突
- 2.3.4 rehash
- 2.3.5 字典的API
- 2.4 跳跃表
- 2.4.1 跳跃表的结构
- 2.4.2 跳跃表的API
- 2.5 整数集合
- 2.5.1 整数集合的结构
- 2.5.2 整数集合的API
- 2.6 压缩列表
- 2.6.1 压缩列表的结构
- 2.6.2 压缩列表结点的结构
- 2.6.3 连锁更新
- 2.6.4 压缩列表API
- 2.7 对象
- 2.7.1 类型
- 2.7.2 编码和底层实现
- 2.7.3 字符串对象
- 2.7.4 列表对象
- 2.7.5 哈希对象
- 2.7.6 集合对象
- 2.7.7 有序集合对象
- 2.7.8 类型检查与命令多态
- 2.7.9 内存回收
- 2.7.10 对象共享
- 2.7.11 对象空转时长
- 3 单机数据库的实现
- 3.1 数据库
- 3.1.1 服务端中的数据库
- 3.1.2 切换数据库
- 3.1.3 数据库键空间
- 3.1.4 过期键的处理
- 3.1.5 数据库通知
- 3.2 RDB持久化
- 操作系统
- 2021-01-08 Linux I/O 操作
- 2021-03-01 Linux 进程控制
- 2021-03-01 Linux 进程通信
- 2021-06-11 Linux 性能优化
- 2021-06-18 性能指标
- 2022-05-05 Android 系统源码阅读笔记
- Java基础
- 2020-07-18 Java 前端编译与优化
- 2020-07-28 Java 虚拟机类加载机制
- 2020-09-11 Java 语法规则
- 2020-09-28 Java 虚拟机字节码执行引擎
- 2020-11-09 class 文件结构
- 2020-12-08 Java 内存模型
- 2021-09-06 Java 并发包
- 代码性能
- 2020-12-03 Java 字符串代码性能
- 2021-01-02 ASM 运行时增强技术
- 理解Unsafe
- Java 8
- 1 行为参数化
- 1.1 行为参数化的实现原理
- 1.2 Java 8中的行为参数化
- 1.3 行为参数化 - 排序
- 1.4 行为参数化 - 线程
- 1.5 泛型实现的行为参数化
- 1.6 小结
- 2 Lambda表达式
- 2.1 Lambda表达式的组成
- 2.2 函数式接口
- 2.2.1 Predicate
- 2.2.2 Consumer
- 2.2.3 Function
- 2.2.4 函数式接口列表
- 2.3 方法引用
- 2.3.1 方法引用的类别
- 2.3.2 构造函数引用
- 2.4 复合方法
- 2.4.1 Comparator复合
- 2.4.2 Predicate复合
- 2.4.3 Function复合
- 3 流处理
- 3.1 流简介
- 3.1.1 流的定义
- 3.1.2 流的特点
- 3.2 流操作
- 3.2.1 中间操作
- 3.2.2 终端操作
- 3.3.3 构建流
- 3.3 流API
- 3.3.1 flatMap的用法
- 3.3.2 reduce的用法
- 3.4 collect操作
- 3.4.1 collect示例
- 3.4.2 Collector接口